@upstash/redis 0.0.0-ci.e9b0c868305a820aaed7d68c8407b277da5a093e-20241008121230 → 0.0.0-ci.e9ff702fe7d2dd2495b4383b5432fe9b93f5a6e7-20250729064656

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.
@@ -25,6 +25,51 @@ var UrlError = class extends Error {
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;
@@ -46,7 +91,7 @@ var HttpClient = class {
46
91
  };
47
92
  this.upstashSyncToken = "";
48
93
  this.readYourWrites = config.readYourWrites ?? true;
49
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
94
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
50
95
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
51
96
  if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
52
97
  throw new UrlError(this.baseUrl);
@@ -73,22 +118,27 @@ var HttpClient = class {
73
118
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
74
119
  }
75
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";
76
126
  const requestOptions = {
77
127
  //@ts-expect-error this should throw due to bun regression
78
128
  cache: this.options.cache,
79
129
  method: "POST",
80
- headers: this.headers,
130
+ headers: requestHeaders,
81
131
  body: JSON.stringify(req.body),
82
132
  keepalive: this.options.keepAlive,
83
133
  agent: this.options.agent,
84
- signal: this.options.signal,
134
+ signal: isSignalFunction ? signal() : signal,
85
135
  /**
86
136
  * Fastly specific
87
137
  */
88
138
  backend: this.options.backend
89
139
  };
90
140
  if (!this.hasCredentials) {
91
- throw new Error(
141
+ console.warn(
92
142
  "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
93
143
  );
94
144
  }
@@ -100,35 +150,71 @@ var HttpClient = class {
100
150
  let error = null;
101
151
  for (let i = 0; i <= this.retry.attempts; i++) {
102
152
  try {
103
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
153
+ res = await fetch(requestUrl, requestOptions);
104
154
  break;
105
155
  } catch (error_) {
106
- if (this.options.signal?.aborted) {
156
+ if (requestOptions.signal?.aborted && isSignalFunction) {
157
+ throw error_;
158
+ } else if (requestOptions.signal?.aborted) {
107
159
  const myBlob = new Blob([
108
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
160
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
109
161
  ]);
110
162
  const myOptions = {
111
163
  status: 200,
112
- statusText: this.options.signal.reason ?? "Aborted"
164
+ statusText: requestOptions.signal.reason ?? "Aborted"
113
165
  };
114
166
  res = new Response(myBlob, myOptions);
115
167
  break;
116
168
  }
117
169
  error = error_;
118
- 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
+ }
119
173
  }
120
174
  }
121
175
  if (!res) {
122
176
  throw error ?? new Error("Exhausted all retries");
123
177
  }
124
- const body = await res.json();
125
178
  if (!res.ok) {
126
- 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)}`);
127
181
  }
128
182
  if (this.readYourWrites) {
129
183
  const headers = res.headers;
130
184
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
131
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();
132
218
  if (this.readYourWrites) {
133
219
  const headers = res.headers;
134
220
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -199,31 +285,6 @@ function merge(obj, key, value) {
199
285
  return obj;
200
286
  }
201
287
 
202
- // pkg/util.ts
203
- function parseRecursive(obj) {
204
- const parsed = Array.isArray(obj) ? obj.map((o) => {
205
- try {
206
- return parseRecursive(o);
207
- } catch {
208
- return o;
209
- }
210
- }) : JSON.parse(obj);
211
- if (typeof parsed === "number" && parsed.toString() !== obj) {
212
- return obj;
213
- }
214
- return parsed;
215
- }
216
- function parseResponse(result) {
217
- try {
218
- return parseRecursive(result);
219
- } catch {
220
- return result;
221
- }
222
- }
223
- function deserializeScanResponse(result) {
224
- return [result[0], ...parseResponse(result.slice(1))];
225
- }
226
-
227
288
  // pkg/commands/command.ts
228
289
  var defaultSerializer = (c) => {
229
290
  switch (typeof c) {
@@ -241,6 +302,11 @@ var Command = class {
241
302
  command;
242
303
  serialize;
243
304
  deserialize;
305
+ headers;
306
+ path;
307
+ onMessage;
308
+ isStreaming;
309
+ signal;
244
310
  /**
245
311
  * Create a new command instance.
246
312
  *
@@ -250,6 +316,11 @@ var Command = class {
250
316
  this.serialize = defaultSerializer;
251
317
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
252
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;
253
324
  if (opts?.latencyLogging) {
254
325
  const originalExec = this.exec.bind(this);
255
326
  this.exec = async (client) => {
@@ -270,7 +341,12 @@ var Command = class {
270
341
  async exec(client) {
271
342
  const { result, error } = await client.request({
272
343
  body: this.command,
273
- 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
274
350
  });
275
351
  if (error) {
276
352
  throw new UpstashError(error);
@@ -288,9 +364,9 @@ function deserialize(result) {
288
364
  return null;
289
365
  }
290
366
  const obj = {};
291
- while (result.length >= 2) {
292
- const key = result.shift();
293
- 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];
294
370
  try {
295
371
  obj[key] = JSON.parse(value);
296
372
  } catch {
@@ -432,6 +508,13 @@ var EchoCommand = class extends Command {
432
508
  }
433
509
  };
434
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
+
435
518
  // pkg/commands/eval.ts
436
519
  var EvalCommand = class extends Command {
437
520
  constructor([script, keys, args], opts) {
@@ -439,6 +522,13 @@ var EvalCommand = class extends Command {
439
522
  }
440
523
  };
441
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
+
442
532
  // pkg/commands/evalsha.ts
443
533
  var EvalshaCommand = class extends Command {
444
534
  constructor([sha, keys, args], opts) {
@@ -446,6 +536,14 @@ var EvalshaCommand = class extends Command {
446
536
  }
447
537
  };
448
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
+
449
547
  // pkg/commands/exists.ts
450
548
  var ExistsCommand = class extends Command {
451
549
  constructor(cmd, opts) {
@@ -662,6 +760,27 @@ var GetDelCommand = class extends Command {
662
760
  }
663
761
  };
664
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
+
665
784
  // pkg/commands/getrange.ts
666
785
  var GetRangeCommand = class extends Command {
667
786
  constructor(cmd, opts) {
@@ -690,6 +809,122 @@ var HExistsCommand = class extends Command {
690
809
  }
691
810
  };
692
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
+
693
928
  // pkg/commands/hget.ts
694
929
  var HGetCommand = class extends Command {
695
930
  constructor(cmd, opts) {
@@ -703,9 +938,9 @@ function deserialize2(result) {
703
938
  return null;
704
939
  }
705
940
  const obj = {};
706
- while (result.length >= 2) {
707
- const key = result.shift();
708
- 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];
709
944
  try {
710
945
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
711
946
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -821,6 +1056,15 @@ var HStrLenCommand = class extends Command {
821
1056
  }
822
1057
  };
823
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
+
824
1068
  // pkg/commands/hvals.ts
825
1069
  var HValsCommand = class extends Command {
826
1070
  constructor(cmd, opts) {
@@ -940,6 +1184,14 @@ var JsonGetCommand = class extends Command {
940
1184
  }
941
1185
  };
942
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
+
943
1195
  // pkg/commands/json_mget.ts
944
1196
  var JsonMGetCommand = class extends Command {
945
1197
  constructor(cmd, opts) {
@@ -1300,11 +1552,14 @@ var ScanCommand = class extends Command {
1300
1552
  if (typeof opts?.count === "number") {
1301
1553
  command.push("count", opts.count);
1302
1554
  }
1303
- 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) {
1304
1558
  command.push("type", opts.type);
1305
1559
  }
1306
1560
  super(command, {
1307
- deserialize: deserializeScanResponse,
1561
+ // @ts-expect-error ignore types here
1562
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1308
1563
  ...cmdOpts
1309
1564
  });
1310
1565
  }
@@ -1730,15 +1985,15 @@ var XPendingCommand = class extends Command {
1730
1985
  function deserialize4(result) {
1731
1986
  const obj = {};
1732
1987
  for (const e of result) {
1733
- while (e.length >= 2) {
1734
- const streamId = e.shift();
1735
- 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];
1736
1991
  if (!(streamId in obj)) {
1737
1992
  obj[streamId] = {};
1738
1993
  }
1739
- while (entries.length >= 2) {
1740
- const field = entries.shift();
1741
- 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];
1742
1997
  try {
1743
1998
  obj[streamId][field] = JSON.parse(value);
1744
1999
  } catch {
@@ -1827,15 +2082,15 @@ var XRevRangeCommand = class extends Command {
1827
2082
  function deserialize5(result) {
1828
2083
  const obj = {};
1829
2084
  for (const e of result) {
1830
- while (e.length >= 2) {
1831
- const streamId = e.shift();
1832
- 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];
1833
2088
  if (!(streamId in obj)) {
1834
2089
  obj[streamId] = {};
1835
2090
  }
1836
- while (entries.length >= 2) {
1837
- const field = entries.shift();
1838
- 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];
1839
2094
  try {
1840
2095
  obj[streamId][field] = JSON.parse(value);
1841
2096
  } catch {
@@ -2122,9 +2377,9 @@ var Pipeline = class {
2122
2377
  this.multiExec = opts.multiExec ?? false;
2123
2378
  if (this.commandOptions?.latencyLogging) {
2124
2379
  const originalExec = this.exec.bind(this);
2125
- this.exec = async () => {
2380
+ this.exec = async (options) => {
2126
2381
  const start = performance.now();
2127
- const result = await originalExec();
2382
+ const result = await (options ? originalExec(options) : originalExec());
2128
2383
  const end = performance.now();
2129
2384
  const loggerResult = (end - start).toFixed(2);
2130
2385
  console.log(
@@ -2134,19 +2389,7 @@ var Pipeline = class {
2134
2389
  };
2135
2390
  }
2136
2391
  }
2137
- /**
2138
- * Send the pipeline request to upstash.
2139
- *
2140
- * Returns an array with the results of all pipelined commands.
2141
- *
2142
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2143
- * ```ts
2144
- * const p = redis.pipeline()
2145
- * p.get("key")
2146
- * const result = p.exec<[{ greeting: string }]>()
2147
- * ```
2148
- */
2149
- exec = async () => {
2392
+ exec = async (options) => {
2150
2393
  if (this.commands.length === 0) {
2151
2394
  throw new Error("Pipeline is empty");
2152
2395
  }
@@ -2155,7 +2398,12 @@ var Pipeline = class {
2155
2398
  path,
2156
2399
  body: Object.values(this.commands).map((c) => c.command)
2157
2400
  });
2158
- 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) => {
2159
2407
  if (error) {
2160
2408
  throw new UpstashError(
2161
2409
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2241,10 +2489,18 @@ var Pipeline = class {
2241
2489
  * @see https://redis.io/commands/echo
2242
2490
  */
2243
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));
2244
2496
  /**
2245
2497
  * @see https://redis.io/commands/eval
2246
2498
  */
2247
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));
2248
2504
  /**
2249
2505
  * @see https://redis.io/commands/evalsha
2250
2506
  */
@@ -2305,6 +2561,10 @@ var Pipeline = class {
2305
2561
  * @see https://redis.io/commands/getdel
2306
2562
  */
2307
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));
2308
2568
  /**
2309
2569
  * @see https://redis.io/commands/getrange
2310
2570
  */
@@ -2321,6 +2581,42 @@ var Pipeline = class {
2321
2581
  * @see https://redis.io/commands/hexists
2322
2582
  */
2323
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));
2324
2620
  /**
2325
2621
  * @see https://redis.io/commands/hget
2326
2622
  */
@@ -2838,6 +3134,10 @@ var Pipeline = class {
2838
3134
  * @see https://redis.io/commands/json.get
2839
3135
  */
2840
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)),
2841
3141
  /**
2842
3142
  * @see https://redis.io/commands/json.mget
2843
3143
  */
@@ -2891,6 +3191,23 @@ var Pipeline = class {
2891
3191
  };
2892
3192
 
2893
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
+ ]);
2894
3211
  function createAutoPipelineProxy(_redis, json) {
2895
3212
  const redis = _redis;
2896
3213
  if (!redis.autoPipelineExecutor) {
@@ -2905,7 +3222,8 @@ function createAutoPipelineProxy(_redis, json) {
2905
3222
  return createAutoPipelineProxy(redis2, true);
2906
3223
  }
2907
3224
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2908
- if (commandInRedisButNotPipeline) {
3225
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3226
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2909
3227
  return redis2[command];
2910
3228
  }
2911
3229
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2949,7 +3267,7 @@ var AutoPipelineExecutor = class {
2949
3267
  executeWithPipeline(pipeline);
2950
3268
  const pipelineDone = this.deferExecution().then(() => {
2951
3269
  if (!this.pipelinePromises.has(pipeline)) {
2952
- const pipelinePromise = pipeline.exec();
3270
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
2953
3271
  this.pipelineCounter += 1;
2954
3272
  this.pipelinePromises.set(pipeline, pipelinePromise);
2955
3273
  this.activePipeline = null;
@@ -2957,7 +3275,11 @@ var AutoPipelineExecutor = class {
2957
3275
  return this.pipelinePromises.get(pipeline);
2958
3276
  });
2959
3277
  const results = await pipelineDone;
2960
- 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;
2961
3283
  }
2962
3284
  async deferExecution() {
2963
3285
  await Promise.resolve();
@@ -2965,28 +3287,221 @@ var AutoPipelineExecutor = class {
2965
3287
  }
2966
3288
  };
2967
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
+
2968
3467
  // pkg/script.ts
2969
- import Hex from "crypto-js/enc-hex.js";
2970
- import sha1 from "crypto-js/sha1.js";
3468
+ import { subtle } from "uncrypto";
2971
3469
  var Script = class {
2972
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
+ */
2973
3478
  sha1;
2974
3479
  redis;
2975
3480
  constructor(redis, script) {
2976
3481
  this.redis = redis;
2977
- this.sha1 = this.digest(script);
2978
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);
2979
3492
  }
2980
3493
  /**
2981
3494
  * Send an `EVAL` command to redis.
2982
3495
  */
2983
3496
  async eval(keys, args) {
3497
+ await this.init(this.script);
2984
3498
  return await this.redis.eval(this.script, keys, args);
2985
3499
  }
2986
3500
  /**
2987
3501
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
2988
3502
  */
2989
3503
  async evalsha(keys, args) {
3504
+ await this.init(this.script);
2990
3505
  return await this.redis.evalsha(this.sha1, keys, args);
2991
3506
  }
2992
3507
  /**
@@ -2996,6 +3511,7 @@ var Script = class {
2996
3511
  * Following calls will be able to use the cached script
2997
3512
  */
2998
3513
  async exec(keys, args) {
3514
+ await this.init(this.script);
2999
3515
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
3000
3516
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3001
3517
  return await this.redis.eval(this.script, keys, args);
@@ -3007,8 +3523,75 @@ var Script = class {
3007
3523
  /**
3008
3524
  * Compute the sha1 hash of the script and return its hex representation.
3009
3525
  */
3010
- digest(s) {
3011
- 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("");
3012
3595
  }
3013
3596
  };
3014
3597
 
@@ -3086,6 +3669,10 @@ var Redis = class {
3086
3669
  * @see https://redis.io/commands/json.get
3087
3670
  */
3088
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),
3089
3676
  /**
3090
3677
  * @see https://redis.io/commands/json.mget
3091
3678
  */
@@ -3155,8 +3742,36 @@ var Redis = class {
3155
3742
  } catch {
3156
3743
  }
3157
3744
  };
3158
- createScript(script) {
3159
- 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);
3160
3775
  }
3161
3776
  /**
3162
3777
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3243,14 +3858,26 @@ var Redis = class {
3243
3858
  * @see https://redis.io/commands/echo
3244
3859
  */
3245
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);
3246
3865
  /**
3247
3866
  * @see https://redis.io/commands/eval
3248
3867
  */
3249
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);
3250
3873
  /**
3251
3874
  * @see https://redis.io/commands/evalsha
3252
3875
  */
3253
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);
3254
3881
  /**
3255
3882
  * @see https://redis.io/commands/exists
3256
3883
  */
@@ -3307,6 +3934,10 @@ var Redis = class {
3307
3934
  * @see https://redis.io/commands/getdel
3308
3935
  */
3309
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);
3310
3941
  /**
3311
3942
  * @see https://redis.io/commands/getrange
3312
3943
  */
@@ -3323,6 +3954,42 @@ var Redis = class {
3323
3954
  * @see https://redis.io/commands/hexists
3324
3955
  */
3325
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);
3326
3993
  /**
3327
3994
  * @see https://redis.io/commands/hget
3328
3995
  */
@@ -3491,6 +4158,13 @@ var Redis = class {
3491
4158
  * @see https://redis.io/commands/psetex
3492
4159
  */
3493
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
+ };
3494
4168
  /**
3495
4169
  * @see https://redis.io/commands/pttl
3496
4170
  */
@@ -3527,10 +4201,9 @@ var Redis = class {
3527
4201
  * @see https://redis.io/commands/sadd
3528
4202
  */
3529
4203
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3530
- /**
3531
- * @see https://redis.io/commands/scan
3532
- */
3533
- 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
+ }
3534
4207
  /**
3535
4208
  * @see https://redis.io/commands/scard
3536
4209
  */
@@ -3619,6 +4292,13 @@ var Redis = class {
3619
4292
  * @see https://redis.io/commands/strlen
3620
4293
  */
3621
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
+ };
3622
4302
  /**
3623
4303
  * @see https://redis.io/commands/sunion
3624
4304
  */