@upstash/redis 0.0.0-ci.998046a844b4998e6b7de8c128d5f41be5545cdf-20241002104435 → 0.0.0-ci.9a0f7fcd9dc38c0a4ce2b192f3f9c3542ccc791c-20251125190254

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.
@@ -8,22 +8,75 @@ var __export = (target, all) => {
8
8
  var error_exports = {};
9
9
  __export(error_exports, {
10
10
  UpstashError: () => UpstashError,
11
+ UpstashJSONParseError: () => UpstashJSONParseError,
11
12
  UrlError: () => UrlError
12
13
  });
13
14
  var UpstashError = class extends Error {
14
- constructor(message) {
15
- super(message);
15
+ constructor(message, options) {
16
+ super(message, options);
16
17
  this.name = "UpstashError";
17
18
  }
18
19
  };
19
20
  var UrlError = class extends Error {
20
21
  constructor(url) {
21
22
  super(
22
- `Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". `
23
+ `Upstash Redis client was passed an invalid URL. You should pass a URL starting with https. Received: "${url}". `
23
24
  );
24
25
  this.name = "UrlError";
25
26
  }
26
27
  };
28
+ var UpstashJSONParseError = class extends UpstashError {
29
+ constructor(body, options) {
30
+ const truncatedBody = body.length > 200 ? body.slice(0, 200) + "..." : body;
31
+ super(`Unable to parse response body: ${truncatedBody}`, options);
32
+ this.name = "UpstashJSONParseError";
33
+ }
34
+ };
35
+
36
+ // pkg/util.ts
37
+ function parseRecursive(obj) {
38
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
39
+ try {
40
+ return parseRecursive(o);
41
+ } catch {
42
+ return o;
43
+ }
44
+ }) : JSON.parse(obj);
45
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
46
+ return obj;
47
+ }
48
+ return parsed;
49
+ }
50
+ function parseResponse(result) {
51
+ try {
52
+ return parseRecursive(result);
53
+ } catch {
54
+ return result;
55
+ }
56
+ }
57
+ function deserializeScanResponse(result) {
58
+ return [result[0], ...parseResponse(result.slice(1))];
59
+ }
60
+ function deserializeScanWithTypesResponse(result) {
61
+ const [cursor, keys] = result;
62
+ const parsedKeys = [];
63
+ for (let i = 0; i < keys.length; i += 2) {
64
+ parsedKeys.push({ key: keys[i], type: keys[i + 1] });
65
+ }
66
+ return [cursor, parsedKeys];
67
+ }
68
+ function mergeHeaders(...headers) {
69
+ const merged = {};
70
+ for (const header of headers) {
71
+ if (!header) continue;
72
+ for (const [key, value] of Object.entries(header)) {
73
+ if (value !== void 0 && value !== null) {
74
+ merged[key] = value;
75
+ }
76
+ }
77
+ }
78
+ return merged;
79
+ }
27
80
 
28
81
  // pkg/http.ts
29
82
  var HttpClient = class {
@@ -32,6 +85,7 @@ var HttpClient = class {
32
85
  options;
33
86
  readYourWrites;
34
87
  upstashSyncToken = "";
88
+ hasCredentials;
35
89
  retry;
36
90
  constructor(config) {
37
91
  this.options = {
@@ -45,15 +99,16 @@ var HttpClient = class {
45
99
  };
46
100
  this.upstashSyncToken = "";
47
101
  this.readYourWrites = config.readYourWrites ?? true;
48
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
102
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
49
103
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
50
- if (!urlRegex.test(this.baseUrl)) {
104
+ if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
51
105
  throw new UrlError(this.baseUrl);
52
106
  }
53
107
  this.headers = {
54
108
  "Content-Type": "application/json",
55
109
  ...config.headers
56
110
  };
111
+ this.hasCredentials = Boolean(this.baseUrl && this.headers.authorization.split(" ")[1]);
57
112
  if (this.options.responseEncoding === "base64") {
58
113
  this.headers["Upstash-Encoding"] = "base64";
59
114
  }
@@ -71,20 +126,30 @@ var HttpClient = class {
71
126
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
72
127
  }
73
128
  async request(req) {
129
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
130
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
131
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
132
+ const signal = req.signal ?? this.options.signal;
133
+ const isSignalFunction = typeof signal === "function";
74
134
  const requestOptions = {
75
135
  //@ts-expect-error this should throw due to bun regression
76
136
  cache: this.options.cache,
77
137
  method: "POST",
78
- headers: this.headers,
138
+ headers: requestHeaders,
79
139
  body: JSON.stringify(req.body),
80
140
  keepalive: this.options.keepAlive,
81
141
  agent: this.options.agent,
82
- signal: this.options.signal,
142
+ signal: isSignalFunction ? signal() : signal,
83
143
  /**
84
144
  * Fastly specific
85
145
  */
86
146
  backend: this.options.backend
87
147
  };
148
+ if (!this.hasCredentials) {
149
+ console.warn(
150
+ "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
151
+ );
152
+ }
88
153
  if (this.readYourWrites) {
89
154
  const newHeader = this.upstashSyncToken;
90
155
  this.headers["upstash-sync-token"] = newHeader;
@@ -93,35 +158,83 @@ var HttpClient = class {
93
158
  let error = null;
94
159
  for (let i = 0; i <= this.retry.attempts; i++) {
95
160
  try {
96
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
161
+ res = await fetch(requestUrl, requestOptions);
97
162
  break;
98
163
  } catch (error_) {
99
- if (this.options.signal?.aborted) {
164
+ if (requestOptions.signal?.aborted && isSignalFunction) {
165
+ throw error_;
166
+ } else if (requestOptions.signal?.aborted) {
100
167
  const myBlob = new Blob([
101
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
168
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
102
169
  ]);
103
170
  const myOptions = {
104
171
  status: 200,
105
- statusText: this.options.signal.reason ?? "Aborted"
172
+ statusText: requestOptions.signal.reason ?? "Aborted"
106
173
  };
107
174
  res = new Response(myBlob, myOptions);
108
175
  break;
109
176
  }
110
177
  error = error_;
111
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
178
+ if (i < this.retry.attempts) {
179
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
180
+ }
112
181
  }
113
182
  }
114
183
  if (!res) {
115
184
  throw error ?? new Error("Exhausted all retries");
116
185
  }
117
- const body = await res.json();
118
186
  if (!res.ok) {
119
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
187
+ let body2;
188
+ const rawBody2 = await res.text();
189
+ try {
190
+ body2 = JSON.parse(rawBody2);
191
+ } catch (error2) {
192
+ throw new UpstashJSONParseError(rawBody2, { cause: error2 });
193
+ }
194
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
120
195
  }
121
196
  if (this.readYourWrites) {
122
197
  const headers = res.headers;
123
198
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
124
199
  }
200
+ if (isEventStream && req && req.onMessage && res.body) {
201
+ const reader = res.body.getReader();
202
+ const decoder = new TextDecoder();
203
+ (async () => {
204
+ try {
205
+ while (true) {
206
+ const { value, done } = await reader.read();
207
+ if (done) break;
208
+ const chunk = decoder.decode(value);
209
+ const lines = chunk.split("\n");
210
+ for (const line of lines) {
211
+ if (line.startsWith("data: ")) {
212
+ const data = line.slice(6);
213
+ req.onMessage?.(data);
214
+ }
215
+ }
216
+ }
217
+ } catch (error2) {
218
+ if (error2 instanceof Error && error2.name === "AbortError") {
219
+ } else {
220
+ console.error("Stream reading error:", error2);
221
+ }
222
+ } finally {
223
+ try {
224
+ await reader.cancel();
225
+ } catch {
226
+ }
227
+ }
228
+ })();
229
+ return { result: 1 };
230
+ }
231
+ let body;
232
+ const rawBody = await res.text();
233
+ try {
234
+ body = JSON.parse(rawBody);
235
+ } catch (error2) {
236
+ throw new UpstashJSONParseError(rawBody, { cause: error2 });
237
+ }
125
238
  if (this.readYourWrites) {
126
239
  const headers = res.headers;
127
240
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -192,31 +305,6 @@ function merge(obj, key, value) {
192
305
  return obj;
193
306
  }
194
307
 
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
308
  // pkg/commands/command.ts
221
309
  var defaultSerializer = (c) => {
222
310
  switch (typeof c) {
@@ -234,6 +322,11 @@ var Command = class {
234
322
  command;
235
323
  serialize;
236
324
  deserialize;
325
+ headers;
326
+ path;
327
+ onMessage;
328
+ isStreaming;
329
+ signal;
237
330
  /**
238
331
  * Create a new command instance.
239
332
  *
@@ -243,6 +336,11 @@ var Command = class {
243
336
  this.serialize = defaultSerializer;
244
337
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
245
338
  this.command = command.map((c) => this.serialize(c));
339
+ this.headers = opts?.headers;
340
+ this.path = opts?.path;
341
+ this.onMessage = opts?.streamOptions?.onMessage;
342
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
343
+ this.signal = opts?.streamOptions?.signal;
246
344
  if (opts?.latencyLogging) {
247
345
  const originalExec = this.exec.bind(this);
248
346
  this.exec = async (client) => {
@@ -263,7 +361,12 @@ var Command = class {
263
361
  async exec(client) {
264
362
  const { result, error } = await client.request({
265
363
  body: this.command,
266
- upstashSyncToken: client.upstashSyncToken
364
+ path: this.path,
365
+ upstashSyncToken: client.upstashSyncToken,
366
+ headers: this.headers,
367
+ onMessage: this.onMessage,
368
+ isStreaming: this.isStreaming,
369
+ signal: this.signal
267
370
  });
268
371
  if (error) {
269
372
  throw new UpstashError(error);
@@ -281,9 +384,9 @@ function deserialize(result) {
281
384
  return null;
282
385
  }
283
386
  const obj = {};
284
- while (result.length >= 2) {
285
- const key = result.shift();
286
- const value = result.shift();
387
+ for (let i = 0; i < result.length; i += 2) {
388
+ const key = result[i];
389
+ const value = result[i + 1];
287
390
  try {
288
391
  obj[key] = JSON.parse(value);
289
392
  } catch {
@@ -425,6 +528,13 @@ var EchoCommand = class extends Command {
425
528
  }
426
529
  };
427
530
 
531
+ // pkg/commands/evalRo.ts
532
+ var EvalROCommand = class extends Command {
533
+ constructor([script, keys, args], opts) {
534
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
535
+ }
536
+ };
537
+
428
538
  // pkg/commands/eval.ts
429
539
  var EvalCommand = class extends Command {
430
540
  constructor([script, keys, args], opts) {
@@ -432,6 +542,13 @@ var EvalCommand = class extends Command {
432
542
  }
433
543
  };
434
544
 
545
+ // pkg/commands/evalshaRo.ts
546
+ var EvalshaROCommand = class extends Command {
547
+ constructor([sha, keys, args], opts) {
548
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
549
+ }
550
+ };
551
+
435
552
  // pkg/commands/evalsha.ts
436
553
  var EvalshaCommand = class extends Command {
437
554
  constructor([sha, keys, args], opts) {
@@ -439,6 +556,14 @@ var EvalshaCommand = class extends Command {
439
556
  }
440
557
  };
441
558
 
559
+ // pkg/commands/exec.ts
560
+ var ExecCommand = class extends Command {
561
+ constructor(cmd, opts) {
562
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
563
+ super(normalizedCmd, opts);
564
+ }
565
+ };
566
+
442
567
  // pkg/commands/exists.ts
443
568
  var ExistsCommand = class extends Command {
444
569
  constructor(cmd, opts) {
@@ -655,6 +780,27 @@ var GetDelCommand = class extends Command {
655
780
  }
656
781
  };
657
782
 
783
+ // pkg/commands/getex.ts
784
+ var GetExCommand = class extends Command {
785
+ constructor([key, opts], cmdOpts) {
786
+ const command = ["getex", key];
787
+ if (opts) {
788
+ if ("ex" in opts && typeof opts.ex === "number") {
789
+ command.push("ex", opts.ex);
790
+ } else if ("px" in opts && typeof opts.px === "number") {
791
+ command.push("px", opts.px);
792
+ } else if ("exat" in opts && typeof opts.exat === "number") {
793
+ command.push("exat", opts.exat);
794
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
795
+ command.push("pxat", opts.pxat);
796
+ } else if ("persist" in opts && opts.persist) {
797
+ command.push("persist");
798
+ }
799
+ }
800
+ super(command, cmdOpts);
801
+ }
802
+ };
803
+
658
804
  // pkg/commands/getrange.ts
659
805
  var GetRangeCommand = class extends Command {
660
806
  constructor(cmd, opts) {
@@ -683,6 +829,122 @@ var HExistsCommand = class extends Command {
683
829
  }
684
830
  };
685
831
 
832
+ // pkg/commands/hexpire.ts
833
+ var HExpireCommand = class extends Command {
834
+ constructor(cmd, opts) {
835
+ const [key, fields, seconds, option] = cmd;
836
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
837
+ super(
838
+ [
839
+ "hexpire",
840
+ key,
841
+ seconds,
842
+ ...option ? [option] : [],
843
+ "FIELDS",
844
+ fieldArray.length,
845
+ ...fieldArray
846
+ ],
847
+ opts
848
+ );
849
+ }
850
+ };
851
+
852
+ // pkg/commands/hexpireat.ts
853
+ var HExpireAtCommand = class extends Command {
854
+ constructor(cmd, opts) {
855
+ const [key, fields, timestamp, option] = cmd;
856
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
857
+ super(
858
+ [
859
+ "hexpireat",
860
+ key,
861
+ timestamp,
862
+ ...option ? [option] : [],
863
+ "FIELDS",
864
+ fieldArray.length,
865
+ ...fieldArray
866
+ ],
867
+ opts
868
+ );
869
+ }
870
+ };
871
+
872
+ // pkg/commands/hexpiretime.ts
873
+ var HExpireTimeCommand = class extends Command {
874
+ constructor(cmd, opts) {
875
+ const [key, fields] = cmd;
876
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
877
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
878
+ }
879
+ };
880
+
881
+ // pkg/commands/hpersist.ts
882
+ var HPersistCommand = class extends Command {
883
+ constructor(cmd, opts) {
884
+ const [key, fields] = cmd;
885
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
886
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
887
+ }
888
+ };
889
+
890
+ // pkg/commands/hpexpire.ts
891
+ var HPExpireCommand = class extends Command {
892
+ constructor(cmd, opts) {
893
+ const [key, fields, milliseconds, option] = cmd;
894
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
895
+ super(
896
+ [
897
+ "hpexpire",
898
+ key,
899
+ milliseconds,
900
+ ...option ? [option] : [],
901
+ "FIELDS",
902
+ fieldArray.length,
903
+ ...fieldArray
904
+ ],
905
+ opts
906
+ );
907
+ }
908
+ };
909
+
910
+ // pkg/commands/hpexpireat.ts
911
+ var HPExpireAtCommand = class extends Command {
912
+ constructor(cmd, opts) {
913
+ const [key, fields, timestamp, option] = cmd;
914
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
915
+ super(
916
+ [
917
+ "hpexpireat",
918
+ key,
919
+ timestamp,
920
+ ...option ? [option] : [],
921
+ "FIELDS",
922
+ fieldArray.length,
923
+ ...fieldArray
924
+ ],
925
+ opts
926
+ );
927
+ }
928
+ };
929
+
930
+ // pkg/commands/hpexpiretime.ts
931
+ var HPExpireTimeCommand = class extends Command {
932
+ constructor(cmd, opts) {
933
+ const [key, fields] = cmd;
934
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
935
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
936
+ }
937
+ };
938
+
939
+ // pkg/commands/hpttl.ts
940
+ var HPTtlCommand = class extends Command {
941
+ constructor(cmd, opts) {
942
+ const [key, fields] = cmd;
943
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
944
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
945
+ }
946
+ };
947
+
686
948
  // pkg/commands/hget.ts
687
949
  var HGetCommand = class extends Command {
688
950
  constructor(cmd, opts) {
@@ -696,9 +958,9 @@ function deserialize2(result) {
696
958
  return null;
697
959
  }
698
960
  const obj = {};
699
- while (result.length >= 2) {
700
- const key = result.shift();
701
- const value = result.shift();
961
+ for (let i = 0; i < result.length; i += 2) {
962
+ const key = result[i];
963
+ const value = result[i + 1];
702
964
  try {
703
965
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
704
966
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -814,6 +1076,15 @@ var HStrLenCommand = class extends Command {
814
1076
  }
815
1077
  };
816
1078
 
1079
+ // pkg/commands/httl.ts
1080
+ var HTtlCommand = class extends Command {
1081
+ constructor(cmd, opts) {
1082
+ const [key, fields] = cmd;
1083
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1084
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1085
+ }
1086
+ };
1087
+
817
1088
  // pkg/commands/hvals.ts
818
1089
  var HValsCommand = class extends Command {
819
1090
  constructor(cmd, opts) {
@@ -933,6 +1204,14 @@ var JsonGetCommand = class extends Command {
933
1204
  }
934
1205
  };
935
1206
 
1207
+ // pkg/commands/json_merge.ts
1208
+ var JsonMergeCommand = class extends Command {
1209
+ constructor(cmd, opts) {
1210
+ const command = ["JSON.MERGE", ...cmd];
1211
+ super(command, opts);
1212
+ }
1213
+ };
1214
+
936
1215
  // pkg/commands/json_mget.ts
937
1216
  var JsonMGetCommand = class extends Command {
938
1217
  constructor(cmd, opts) {
@@ -1293,11 +1572,14 @@ var ScanCommand = class extends Command {
1293
1572
  if (typeof opts?.count === "number") {
1294
1573
  command.push("count", opts.count);
1295
1574
  }
1296
- if (opts?.type && opts.type.length > 0) {
1575
+ if (opts && "withType" in opts && opts.withType === true) {
1576
+ command.push("withtype");
1577
+ } else if (opts && "type" in opts && opts.type && opts.type.length > 0) {
1297
1578
  command.push("type", opts.type);
1298
1579
  }
1299
1580
  super(command, {
1300
- deserialize: deserializeScanResponse,
1581
+ // @ts-expect-error ignore types here
1582
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1301
1583
  ...cmdOpts
1302
1584
  });
1303
1585
  }
@@ -1723,15 +2005,15 @@ var XPendingCommand = class extends Command {
1723
2005
  function deserialize4(result) {
1724
2006
  const obj = {};
1725
2007
  for (const e of result) {
1726
- while (e.length >= 2) {
1727
- const streamId = e.shift();
1728
- const entries = e.shift();
2008
+ for (let i = 0; i < e.length; i += 2) {
2009
+ const streamId = e[i];
2010
+ const entries = e[i + 1];
1729
2011
  if (!(streamId in obj)) {
1730
2012
  obj[streamId] = {};
1731
2013
  }
1732
- while (entries.length >= 2) {
1733
- const field = entries.shift();
1734
- const value = entries.shift();
2014
+ for (let j = 0; j < entries.length; j += 2) {
2015
+ const field = entries[j];
2016
+ const value = entries[j + 1];
1735
2017
  try {
1736
2018
  obj[streamId][field] = JSON.parse(value);
1737
2019
  } catch {
@@ -1820,15 +2102,15 @@ var XRevRangeCommand = class extends Command {
1820
2102
  function deserialize5(result) {
1821
2103
  const obj = {};
1822
2104
  for (const e of result) {
1823
- while (e.length >= 2) {
1824
- const streamId = e.shift();
1825
- const entries = e.shift();
2105
+ for (let i = 0; i < e.length; i += 2) {
2106
+ const streamId = e[i];
2107
+ const entries = e[i + 1];
1826
2108
  if (!(streamId in obj)) {
1827
2109
  obj[streamId] = {};
1828
2110
  }
1829
- while (entries.length >= 2) {
1830
- const field = entries.shift();
1831
- const value = entries.shift();
2111
+ for (let j = 0; j < entries.length; j += 2) {
2112
+ const field = entries[j];
2113
+ const value = entries[j + 1];
1832
2114
  try {
1833
2115
  obj[streamId][field] = JSON.parse(value);
1834
2116
  } catch {
@@ -2115,9 +2397,9 @@ var Pipeline = class {
2115
2397
  this.multiExec = opts.multiExec ?? false;
2116
2398
  if (this.commandOptions?.latencyLogging) {
2117
2399
  const originalExec = this.exec.bind(this);
2118
- this.exec = async () => {
2400
+ this.exec = async (options) => {
2119
2401
  const start = performance.now();
2120
- const result = await originalExec();
2402
+ const result = await (options ? originalExec(options) : originalExec());
2121
2403
  const end = performance.now();
2122
2404
  const loggerResult = (end - start).toFixed(2);
2123
2405
  console.log(
@@ -2127,19 +2409,7 @@ var Pipeline = class {
2127
2409
  };
2128
2410
  }
2129
2411
  }
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 () => {
2412
+ exec = async (options) => {
2143
2413
  if (this.commands.length === 0) {
2144
2414
  throw new Error("Pipeline is empty");
2145
2415
  }
@@ -2148,7 +2418,12 @@ var Pipeline = class {
2148
2418
  path,
2149
2419
  body: Object.values(this.commands).map((c) => c.command)
2150
2420
  });
2151
- return res.map(({ error, result }, i) => {
2421
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2422
+ return {
2423
+ error,
2424
+ result: this.commands[i].deserialize(result)
2425
+ };
2426
+ }) : res.map(({ error, result }, i) => {
2152
2427
  if (error) {
2153
2428
  throw new UpstashError(
2154
2429
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2234,10 +2509,18 @@ var Pipeline = class {
2234
2509
  * @see https://redis.io/commands/echo
2235
2510
  */
2236
2511
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2512
+ /**
2513
+ * @see https://redis.io/commands/eval_ro
2514
+ */
2515
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2237
2516
  /**
2238
2517
  * @see https://redis.io/commands/eval
2239
2518
  */
2240
2519
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2520
+ /**
2521
+ * @see https://redis.io/commands/evalsha_ro
2522
+ */
2523
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2241
2524
  /**
2242
2525
  * @see https://redis.io/commands/evalsha
2243
2526
  */
@@ -2298,6 +2581,10 @@ var Pipeline = class {
2298
2581
  * @see https://redis.io/commands/getdel
2299
2582
  */
2300
2583
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2584
+ /**
2585
+ * @see https://redis.io/commands/getex
2586
+ */
2587
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2301
2588
  /**
2302
2589
  * @see https://redis.io/commands/getrange
2303
2590
  */
@@ -2314,6 +2601,42 @@ var Pipeline = class {
2314
2601
  * @see https://redis.io/commands/hexists
2315
2602
  */
2316
2603
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2604
+ /**
2605
+ * @see https://redis.io/commands/hexpire
2606
+ */
2607
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2608
+ /**
2609
+ * @see https://redis.io/commands/hexpireat
2610
+ */
2611
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2612
+ /**
2613
+ * @see https://redis.io/commands/hexpiretime
2614
+ */
2615
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2616
+ /**
2617
+ * @see https://redis.io/commands/httl
2618
+ */
2619
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2620
+ /**
2621
+ * @see https://redis.io/commands/hpexpire
2622
+ */
2623
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2624
+ /**
2625
+ * @see https://redis.io/commands/hpexpireat
2626
+ */
2627
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2628
+ /**
2629
+ * @see https://redis.io/commands/hpexpiretime
2630
+ */
2631
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2632
+ /**
2633
+ * @see https://redis.io/commands/hpttl
2634
+ */
2635
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2636
+ /**
2637
+ * @see https://redis.io/commands/hpersist
2638
+ */
2639
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2317
2640
  /**
2318
2641
  * @see https://redis.io/commands/hget
2319
2642
  */
@@ -2831,6 +3154,10 @@ var Pipeline = class {
2831
3154
  * @see https://redis.io/commands/json.get
2832
3155
  */
2833
3156
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3157
+ /**
3158
+ * @see https://redis.io/commands/json.merge
3159
+ */
3160
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2834
3161
  /**
2835
3162
  * @see https://redis.io/commands/json.mget
2836
3163
  */
@@ -2884,6 +3211,24 @@ var Pipeline = class {
2884
3211
  };
2885
3212
 
2886
3213
  // pkg/auto-pipeline.ts
3214
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
3215
+ "scan",
3216
+ "keys",
3217
+ "flushdb",
3218
+ "flushall",
3219
+ "dbsize",
3220
+ "hscan",
3221
+ "hgetall",
3222
+ "hkeys",
3223
+ "lrange",
3224
+ "sscan",
3225
+ "smembers",
3226
+ "xrange",
3227
+ "xrevrange",
3228
+ "zscan",
3229
+ "zrange",
3230
+ "exec"
3231
+ ]);
2887
3232
  function createAutoPipelineProxy(_redis, json) {
2888
3233
  const redis = _redis;
2889
3234
  if (!redis.autoPipelineExecutor) {
@@ -2898,7 +3243,8 @@ function createAutoPipelineProxy(_redis, json) {
2898
3243
  return createAutoPipelineProxy(redis2, true);
2899
3244
  }
2900
3245
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2901
- if (commandInRedisButNotPipeline) {
3246
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3247
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2902
3248
  return redis2[command];
2903
3249
  }
2904
3250
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2942,7 +3288,7 @@ var AutoPipelineExecutor = class {
2942
3288
  executeWithPipeline(pipeline);
2943
3289
  const pipelineDone = this.deferExecution().then(() => {
2944
3290
  if (!this.pipelinePromises.has(pipeline)) {
2945
- const pipelinePromise = pipeline.exec();
3291
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
2946
3292
  this.pipelineCounter += 1;
2947
3293
  this.pipelinePromises.set(pipeline, pipelinePromise);
2948
3294
  this.activePipeline = null;
@@ -2950,7 +3296,11 @@ var AutoPipelineExecutor = class {
2950
3296
  return this.pipelinePromises.get(pipeline);
2951
3297
  });
2952
3298
  const results = await pipelineDone;
2953
- return results[index];
3299
+ const commandResult = results[index];
3300
+ if (commandResult.error) {
3301
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
3302
+ }
3303
+ return commandResult.result;
2954
3304
  }
2955
3305
  async deferExecution() {
2956
3306
  await Promise.resolve();
@@ -2958,28 +3308,230 @@ var AutoPipelineExecutor = class {
2958
3308
  }
2959
3309
  };
2960
3310
 
3311
+ // pkg/commands/psubscribe.ts
3312
+ var PSubscribeCommand = class extends Command {
3313
+ constructor(cmd, opts) {
3314
+ const sseHeaders = {
3315
+ Accept: "text/event-stream",
3316
+ "Cache-Control": "no-cache",
3317
+ Connection: "keep-alive"
3318
+ };
3319
+ super([], {
3320
+ ...opts,
3321
+ headers: sseHeaders,
3322
+ path: ["psubscribe", ...cmd],
3323
+ streamOptions: {
3324
+ isStreaming: true,
3325
+ onMessage: opts?.streamOptions?.onMessage,
3326
+ signal: opts?.streamOptions?.signal
3327
+ }
3328
+ });
3329
+ }
3330
+ };
3331
+
3332
+ // pkg/commands/subscribe.ts
3333
+ var Subscriber = class extends EventTarget {
3334
+ subscriptions;
3335
+ client;
3336
+ listeners;
3337
+ opts;
3338
+ constructor(client, channels, isPattern = false, opts) {
3339
+ super();
3340
+ this.client = client;
3341
+ this.subscriptions = /* @__PURE__ */ new Map();
3342
+ this.listeners = /* @__PURE__ */ new Map();
3343
+ this.opts = opts;
3344
+ for (const channel of channels) {
3345
+ if (isPattern) {
3346
+ this.subscribeToPattern(channel);
3347
+ } else {
3348
+ this.subscribeToChannel(channel);
3349
+ }
3350
+ }
3351
+ }
3352
+ subscribeToChannel(channel) {
3353
+ const controller = new AbortController();
3354
+ const command = new SubscribeCommand([channel], {
3355
+ streamOptions: {
3356
+ signal: controller.signal,
3357
+ onMessage: (data) => this.handleMessage(data, false)
3358
+ }
3359
+ });
3360
+ command.exec(this.client).catch((error) => {
3361
+ if (error.name !== "AbortError") {
3362
+ this.dispatchToListeners("error", error);
3363
+ }
3364
+ });
3365
+ this.subscriptions.set(channel, {
3366
+ command,
3367
+ controller,
3368
+ isPattern: false
3369
+ });
3370
+ }
3371
+ subscribeToPattern(pattern) {
3372
+ const controller = new AbortController();
3373
+ const command = new PSubscribeCommand([pattern], {
3374
+ streamOptions: {
3375
+ signal: controller.signal,
3376
+ onMessage: (data) => this.handleMessage(data, true)
3377
+ }
3378
+ });
3379
+ command.exec(this.client).catch((error) => {
3380
+ if (error.name !== "AbortError") {
3381
+ this.dispatchToListeners("error", error);
3382
+ }
3383
+ });
3384
+ this.subscriptions.set(pattern, {
3385
+ command,
3386
+ controller,
3387
+ isPattern: true
3388
+ });
3389
+ }
3390
+ handleMessage(data, isPattern) {
3391
+ const messageData = data.replace(/^data:\s*/, "");
3392
+ const firstCommaIndex = messageData.indexOf(",");
3393
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
3394
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
3395
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
3396
+ const type = messageData.slice(0, firstCommaIndex);
3397
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
3398
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3399
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
3400
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
3401
+ try {
3402
+ const message = this.opts?.automaticDeserialization === false ? messageStr : JSON.parse(messageStr);
3403
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
3404
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
3405
+ } catch (error) {
3406
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3407
+ }
3408
+ } else {
3409
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3410
+ const messageStr = messageData.slice(secondCommaIndex + 1);
3411
+ try {
3412
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
3413
+ const count = Number.parseInt(messageStr);
3414
+ this.dispatchToListeners(type, count);
3415
+ } else {
3416
+ const message = this.opts?.automaticDeserialization === false ? messageStr : parseWithTryCatch(messageStr);
3417
+ this.dispatchToListeners(type, { channel, message });
3418
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
3419
+ }
3420
+ } catch (error) {
3421
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3422
+ }
3423
+ }
3424
+ }
3425
+ }
3426
+ dispatchToListeners(type, data) {
3427
+ const listeners = this.listeners.get(type);
3428
+ if (listeners) {
3429
+ for (const listener of listeners) {
3430
+ listener(data);
3431
+ }
3432
+ }
3433
+ }
3434
+ on(type, listener) {
3435
+ if (!this.listeners.has(type)) {
3436
+ this.listeners.set(type, /* @__PURE__ */ new Set());
3437
+ }
3438
+ this.listeners.get(type)?.add(listener);
3439
+ }
3440
+ removeAllListeners() {
3441
+ this.listeners.clear();
3442
+ }
3443
+ async unsubscribe(channels) {
3444
+ if (channels) {
3445
+ for (const channel of channels) {
3446
+ const subscription = this.subscriptions.get(channel);
3447
+ if (subscription) {
3448
+ try {
3449
+ subscription.controller.abort();
3450
+ } catch {
3451
+ }
3452
+ this.subscriptions.delete(channel);
3453
+ }
3454
+ }
3455
+ } else {
3456
+ for (const subscription of this.subscriptions.values()) {
3457
+ try {
3458
+ subscription.controller.abort();
3459
+ } catch {
3460
+ }
3461
+ }
3462
+ this.subscriptions.clear();
3463
+ this.removeAllListeners();
3464
+ }
3465
+ }
3466
+ getSubscribedChannels() {
3467
+ return [...this.subscriptions.keys()];
3468
+ }
3469
+ };
3470
+ var SubscribeCommand = class extends Command {
3471
+ constructor(cmd, opts) {
3472
+ const sseHeaders = {
3473
+ Accept: "text/event-stream",
3474
+ "Cache-Control": "no-cache",
3475
+ Connection: "keep-alive"
3476
+ };
3477
+ super([], {
3478
+ ...opts,
3479
+ headers: sseHeaders,
3480
+ path: ["subscribe", ...cmd],
3481
+ streamOptions: {
3482
+ isStreaming: true,
3483
+ onMessage: opts?.streamOptions?.onMessage,
3484
+ signal: opts?.streamOptions?.signal
3485
+ }
3486
+ });
3487
+ }
3488
+ };
3489
+ var parseWithTryCatch = (str) => {
3490
+ try {
3491
+ return JSON.parse(str);
3492
+ } catch {
3493
+ return str;
3494
+ }
3495
+ };
3496
+
2961
3497
  // pkg/script.ts
2962
- import Hex from "crypto-js/enc-hex.js";
2963
- import sha1 from "crypto-js/sha1.js";
3498
+ import { subtle } from "uncrypto";
2964
3499
  var Script = class {
2965
3500
  script;
3501
+ /**
3502
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3503
+ * asynchronously. Do not use this property immidiately after the constructor.
3504
+ *
3505
+ * This property is only exposed for backwards compatibility and will be removed in the
3506
+ * future major release.
3507
+ */
2966
3508
  sha1;
2967
3509
  redis;
2968
3510
  constructor(redis, script) {
2969
3511
  this.redis = redis;
2970
- this.sha1 = this.digest(script);
2971
3512
  this.script = script;
3513
+ this.sha1 = "";
3514
+ void this.init(script);
3515
+ }
3516
+ /**
3517
+ * Initialize the script by computing its SHA-1 hash.
3518
+ */
3519
+ async init(script) {
3520
+ if (this.sha1) return;
3521
+ this.sha1 = await this.digest(script);
2972
3522
  }
2973
3523
  /**
2974
3524
  * Send an `EVAL` command to redis.
2975
3525
  */
2976
3526
  async eval(keys, args) {
3527
+ await this.init(this.script);
2977
3528
  return await this.redis.eval(this.script, keys, args);
2978
3529
  }
2979
3530
  /**
2980
3531
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
2981
3532
  */
2982
3533
  async evalsha(keys, args) {
3534
+ await this.init(this.script);
2983
3535
  return await this.redis.evalsha(this.sha1, keys, args);
2984
3536
  }
2985
3537
  /**
@@ -2989,6 +3541,7 @@ var Script = class {
2989
3541
  * Following calls will be able to use the cached script
2990
3542
  */
2991
3543
  async exec(keys, args) {
3544
+ await this.init(this.script);
2992
3545
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
2993
3546
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
2994
3547
  return await this.redis.eval(this.script, keys, args);
@@ -3000,8 +3553,75 @@ var Script = class {
3000
3553
  /**
3001
3554
  * Compute the sha1 hash of the script and return its hex representation.
3002
3555
  */
3003
- digest(s) {
3004
- return Hex.stringify(sha1(s));
3556
+ async digest(s) {
3557
+ const data = new TextEncoder().encode(s);
3558
+ const hashBuffer = await subtle.digest("SHA-1", data);
3559
+ const hashArray = [...new Uint8Array(hashBuffer)];
3560
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3561
+ }
3562
+ };
3563
+
3564
+ // pkg/scriptRo.ts
3565
+ import { subtle as subtle2 } from "uncrypto";
3566
+ var ScriptRO = class {
3567
+ script;
3568
+ /**
3569
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3570
+ * asynchronously. Do not use this property immidiately after the constructor.
3571
+ *
3572
+ * This property is only exposed for backwards compatibility and will be removed in the
3573
+ * future major release.
3574
+ */
3575
+ sha1;
3576
+ redis;
3577
+ constructor(redis, script) {
3578
+ this.redis = redis;
3579
+ this.sha1 = "";
3580
+ this.script = script;
3581
+ void this.init(script);
3582
+ }
3583
+ async init(script) {
3584
+ if (this.sha1) return;
3585
+ this.sha1 = await this.digest(script);
3586
+ }
3587
+ /**
3588
+ * Send an `EVAL_RO` command to redis.
3589
+ */
3590
+ async evalRo(keys, args) {
3591
+ await this.init(this.script);
3592
+ return await this.redis.evalRo(this.script, keys, args);
3593
+ }
3594
+ /**
3595
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3596
+ */
3597
+ async evalshaRo(keys, args) {
3598
+ await this.init(this.script);
3599
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3600
+ }
3601
+ /**
3602
+ * Optimistically try to run `EVALSHA_RO` first.
3603
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3604
+ *
3605
+ * Following calls will be able to use the cached script
3606
+ */
3607
+ async exec(keys, args) {
3608
+ await this.init(this.script);
3609
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3610
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3611
+ return await this.redis.evalRo(this.script, keys, args);
3612
+ }
3613
+ throw error;
3614
+ });
3615
+ return res;
3616
+ }
3617
+ /**
3618
+ * Compute the sha1 hash of the script and return its hex representation.
3619
+ */
3620
+ async digest(s) {
3621
+ const data = new TextEncoder().encode(s);
3622
+ const hashBuffer = await subtle2.digest("SHA-1", data);
3623
+ const hashArray = [...new Uint8Array(hashBuffer)];
3624
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3005
3625
  }
3006
3626
  };
3007
3627
 
@@ -3079,6 +3699,10 @@ var Redis = class {
3079
3699
  * @see https://redis.io/commands/json.get
3080
3700
  */
3081
3701
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3702
+ /**
3703
+ * @see https://redis.io/commands/json.merge
3704
+ */
3705
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3082
3706
  /**
3083
3707
  * @see https://redis.io/commands/json.mget
3084
3708
  */
@@ -3148,8 +3772,36 @@ var Redis = class {
3148
3772
  } catch {
3149
3773
  }
3150
3774
  };
3151
- createScript(script) {
3152
- return new Script(this, script);
3775
+ /**
3776
+ * Creates a new script.
3777
+ *
3778
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3779
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3780
+ * the entire script. Afterwards, the script is cached on the server.
3781
+ *
3782
+ * @param script - The script to create
3783
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3784
+ * @returns A new script
3785
+ *
3786
+ * @example
3787
+ * ```ts
3788
+ * const redis = new Redis({...})
3789
+ *
3790
+ * const script = redis.createScript<string>("return ARGV[1];")
3791
+ * const arg1 = await script.eval([], ["Hello World"])
3792
+ * expect(arg1, "Hello World")
3793
+ * ```
3794
+ * @example
3795
+ * ```ts
3796
+ * const redis = new Redis({...})
3797
+ *
3798
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3799
+ * const arg1 = await script.evalRo([], ["Hello World"])
3800
+ * expect(arg1, "Hello World")
3801
+ * ```
3802
+ */
3803
+ createScript(script, opts) {
3804
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3153
3805
  }
3154
3806
  /**
3155
3807
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3236,14 +3888,26 @@ var Redis = class {
3236
3888
  * @see https://redis.io/commands/echo
3237
3889
  */
3238
3890
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3891
+ /**
3892
+ * @see https://redis.io/commands/eval_ro
3893
+ */
3894
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3239
3895
  /**
3240
3896
  * @see https://redis.io/commands/eval
3241
3897
  */
3242
3898
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3899
+ /**
3900
+ * @see https://redis.io/commands/evalsha_ro
3901
+ */
3902
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3243
3903
  /**
3244
3904
  * @see https://redis.io/commands/evalsha
3245
3905
  */
3246
3906
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3907
+ /**
3908
+ * Generic method to execute any Redis command.
3909
+ */
3910
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3247
3911
  /**
3248
3912
  * @see https://redis.io/commands/exists
3249
3913
  */
@@ -3300,6 +3964,10 @@ var Redis = class {
3300
3964
  * @see https://redis.io/commands/getdel
3301
3965
  */
3302
3966
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3967
+ /**
3968
+ * @see https://redis.io/commands/getex
3969
+ */
3970
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3303
3971
  /**
3304
3972
  * @see https://redis.io/commands/getrange
3305
3973
  */
@@ -3316,6 +3984,42 @@ var Redis = class {
3316
3984
  * @see https://redis.io/commands/hexists
3317
3985
  */
3318
3986
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3987
+ /**
3988
+ * @see https://redis.io/commands/hexpire
3989
+ */
3990
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3991
+ /**
3992
+ * @see https://redis.io/commands/hexpireat
3993
+ */
3994
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3995
+ /**
3996
+ * @see https://redis.io/commands/hexpiretime
3997
+ */
3998
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3999
+ /**
4000
+ * @see https://redis.io/commands/httl
4001
+ */
4002
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
4003
+ /**
4004
+ * @see https://redis.io/commands/hpexpire
4005
+ */
4006
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
4007
+ /**
4008
+ * @see https://redis.io/commands/hpexpireat
4009
+ */
4010
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
4011
+ /**
4012
+ * @see https://redis.io/commands/hpexpiretime
4013
+ */
4014
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
4015
+ /**
4016
+ * @see https://redis.io/commands/hpttl
4017
+ */
4018
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
4019
+ /**
4020
+ * @see https://redis.io/commands/hpersist
4021
+ */
4022
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3319
4023
  /**
3320
4024
  * @see https://redis.io/commands/hget
3321
4025
  */
@@ -3484,6 +4188,13 @@ var Redis = class {
3484
4188
  * @see https://redis.io/commands/psetex
3485
4189
  */
3486
4190
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4191
+ /**
4192
+ * @see https://redis.io/commands/psubscribe
4193
+ */
4194
+ psubscribe = (patterns) => {
4195
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4196
+ return new Subscriber(this.client, patternArray, true, this.opts);
4197
+ };
3487
4198
  /**
3488
4199
  * @see https://redis.io/commands/pttl
3489
4200
  */
@@ -3520,10 +4231,9 @@ var Redis = class {
3520
4231
  * @see https://redis.io/commands/sadd
3521
4232
  */
3522
4233
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3523
- /**
3524
- * @see https://redis.io/commands/scan
3525
- */
3526
- scan = (...args) => new ScanCommand(args, this.opts).exec(this.client);
4234
+ scan(cursor, opts) {
4235
+ return new ScanCommand([cursor, opts], this.opts).exec(this.client);
4236
+ }
3527
4237
  /**
3528
4238
  * @see https://redis.io/commands/scard
3529
4239
  */
@@ -3612,6 +4322,13 @@ var Redis = class {
3612
4322
  * @see https://redis.io/commands/strlen
3613
4323
  */
3614
4324
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4325
+ /**
4326
+ * @see https://redis.io/commands/subscribe
4327
+ */
4328
+ subscribe = (channels) => {
4329
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4330
+ return new Subscriber(this.client, channelArray, false, this.opts);
4331
+ };
3615
4332
  /**
3616
4333
  * @see https://redis.io/commands/sunion
3617
4334
  */