@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.
package/cloudflare.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // platforms/cloudflare.ts
@@ -56,6 +46,51 @@ var UrlError = class extends Error {
56
46
  }
57
47
  };
58
48
 
49
+ // pkg/util.ts
50
+ function parseRecursive(obj) {
51
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
52
+ try {
53
+ return parseRecursive(o);
54
+ } catch {
55
+ return o;
56
+ }
57
+ }) : JSON.parse(obj);
58
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
59
+ return obj;
60
+ }
61
+ return parsed;
62
+ }
63
+ function parseResponse(result) {
64
+ try {
65
+ return parseRecursive(result);
66
+ } catch {
67
+ return result;
68
+ }
69
+ }
70
+ function deserializeScanResponse(result) {
71
+ return [result[0], ...parseResponse(result.slice(1))];
72
+ }
73
+ function deserializeScanWithTypesResponse(result) {
74
+ const [cursor, keys] = result;
75
+ const parsedKeys = [];
76
+ for (let i = 0; i < keys.length; i += 2) {
77
+ parsedKeys.push({ key: keys[i], type: keys[i + 1] });
78
+ }
79
+ return [cursor, parsedKeys];
80
+ }
81
+ function mergeHeaders(...headers) {
82
+ const merged = {};
83
+ for (const header of headers) {
84
+ if (!header) continue;
85
+ for (const [key, value] of Object.entries(header)) {
86
+ if (value !== void 0 && value !== null) {
87
+ merged[key] = value;
88
+ }
89
+ }
90
+ }
91
+ return merged;
92
+ }
93
+
59
94
  // pkg/http.ts
60
95
  var HttpClient = class {
61
96
  baseUrl;
@@ -77,7 +112,7 @@ var HttpClient = class {
77
112
  };
78
113
  this.upstashSyncToken = "";
79
114
  this.readYourWrites = config.readYourWrites ?? true;
80
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
115
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
81
116
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
82
117
  if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
83
118
  throw new UrlError(this.baseUrl);
@@ -104,22 +139,27 @@ var HttpClient = class {
104
139
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
105
140
  }
106
141
  async request(req) {
142
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
143
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
144
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
145
+ const signal = req.signal ?? this.options.signal;
146
+ const isSignalFunction = typeof signal === "function";
107
147
  const requestOptions = {
108
148
  //@ts-expect-error this should throw due to bun regression
109
149
  cache: this.options.cache,
110
150
  method: "POST",
111
- headers: this.headers,
151
+ headers: requestHeaders,
112
152
  body: JSON.stringify(req.body),
113
153
  keepalive: this.options.keepAlive,
114
154
  agent: this.options.agent,
115
- signal: this.options.signal,
155
+ signal: isSignalFunction ? signal() : signal,
116
156
  /**
117
157
  * Fastly specific
118
158
  */
119
159
  backend: this.options.backend
120
160
  };
121
161
  if (!this.hasCredentials) {
122
- throw new Error(
162
+ console.warn(
123
163
  "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
124
164
  );
125
165
  }
@@ -131,35 +171,71 @@ var HttpClient = class {
131
171
  let error = null;
132
172
  for (let i = 0; i <= this.retry.attempts; i++) {
133
173
  try {
134
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
174
+ res = await fetch(requestUrl, requestOptions);
135
175
  break;
136
176
  } catch (error_) {
137
- if (this.options.signal?.aborted) {
177
+ if (requestOptions.signal?.aborted && isSignalFunction) {
178
+ throw error_;
179
+ } else if (requestOptions.signal?.aborted) {
138
180
  const myBlob = new Blob([
139
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
181
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
140
182
  ]);
141
183
  const myOptions = {
142
184
  status: 200,
143
- statusText: this.options.signal.reason ?? "Aborted"
185
+ statusText: requestOptions.signal.reason ?? "Aborted"
144
186
  };
145
187
  res = new Response(myBlob, myOptions);
146
188
  break;
147
189
  }
148
190
  error = error_;
149
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
191
+ if (i < this.retry.attempts) {
192
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
193
+ }
150
194
  }
151
195
  }
152
196
  if (!res) {
153
197
  throw error ?? new Error("Exhausted all retries");
154
198
  }
155
- const body = await res.json();
156
199
  if (!res.ok) {
157
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
200
+ const body2 = await res.json();
201
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
158
202
  }
159
203
  if (this.readYourWrites) {
160
204
  const headers = res.headers;
161
205
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
162
206
  }
207
+ if (isEventStream && req && req.onMessage && res.body) {
208
+ const reader = res.body.getReader();
209
+ const decoder = new TextDecoder();
210
+ (async () => {
211
+ try {
212
+ while (true) {
213
+ const { value, done } = await reader.read();
214
+ if (done) break;
215
+ const chunk = decoder.decode(value);
216
+ const lines = chunk.split("\n");
217
+ for (const line of lines) {
218
+ if (line.startsWith("data: ")) {
219
+ const data = line.slice(6);
220
+ req.onMessage?.(data);
221
+ }
222
+ }
223
+ }
224
+ } catch (error2) {
225
+ if (error2 instanceof Error && error2.name === "AbortError") {
226
+ } else {
227
+ console.error("Stream reading error:", error2);
228
+ }
229
+ } finally {
230
+ try {
231
+ await reader.cancel();
232
+ } catch {
233
+ }
234
+ }
235
+ })();
236
+ return { result: 1 };
237
+ }
238
+ const body = await res.json();
163
239
  if (this.readYourWrites) {
164
240
  const headers = res.headers;
165
241
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -231,6 +307,23 @@ function merge(obj, key, value) {
231
307
  }
232
308
 
233
309
  // pkg/auto-pipeline.ts
310
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
311
+ "scan",
312
+ "keys",
313
+ "flushdb",
314
+ "flushall",
315
+ "dbsize",
316
+ "hscan",
317
+ "hgetall",
318
+ "hkeys",
319
+ "lrange",
320
+ "sscan",
321
+ "smembers",
322
+ "xrange",
323
+ "xrevrange",
324
+ "zscan",
325
+ "zrange"
326
+ ]);
234
327
  function createAutoPipelineProxy(_redis, json) {
235
328
  const redis = _redis;
236
329
  if (!redis.autoPipelineExecutor) {
@@ -245,7 +338,8 @@ function createAutoPipelineProxy(_redis, json) {
245
338
  return createAutoPipelineProxy(redis2, true);
246
339
  }
247
340
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
248
- if (commandInRedisButNotPipeline) {
341
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
342
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
249
343
  return redis2[command];
250
344
  }
251
345
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -289,7 +383,7 @@ var AutoPipelineExecutor = class {
289
383
  executeWithPipeline(pipeline);
290
384
  const pipelineDone = this.deferExecution().then(() => {
291
385
  if (!this.pipelinePromises.has(pipeline)) {
292
- const pipelinePromise = pipeline.exec();
386
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
293
387
  this.pipelineCounter += 1;
294
388
  this.pipelinePromises.set(pipeline, pipelinePromise);
295
389
  this.activePipeline = null;
@@ -297,7 +391,11 @@ var AutoPipelineExecutor = class {
297
391
  return this.pipelinePromises.get(pipeline);
298
392
  });
299
393
  const results = await pipelineDone;
300
- return results[index];
394
+ const commandResult = results[index];
395
+ if (commandResult.error) {
396
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
397
+ }
398
+ return commandResult.result;
301
399
  }
302
400
  async deferExecution() {
303
401
  await Promise.resolve();
@@ -305,31 +403,6 @@ var AutoPipelineExecutor = class {
305
403
  }
306
404
  };
307
405
 
308
- // pkg/util.ts
309
- function parseRecursive(obj) {
310
- const parsed = Array.isArray(obj) ? obj.map((o) => {
311
- try {
312
- return parseRecursive(o);
313
- } catch {
314
- return o;
315
- }
316
- }) : JSON.parse(obj);
317
- if (typeof parsed === "number" && parsed.toString() !== obj) {
318
- return obj;
319
- }
320
- return parsed;
321
- }
322
- function parseResponse(result) {
323
- try {
324
- return parseRecursive(result);
325
- } catch {
326
- return result;
327
- }
328
- }
329
- function deserializeScanResponse(result) {
330
- return [result[0], ...parseResponse(result.slice(1))];
331
- }
332
-
333
406
  // pkg/commands/command.ts
334
407
  var defaultSerializer = (c) => {
335
408
  switch (typeof c) {
@@ -347,6 +420,11 @@ var Command = class {
347
420
  command;
348
421
  serialize;
349
422
  deserialize;
423
+ headers;
424
+ path;
425
+ onMessage;
426
+ isStreaming;
427
+ signal;
350
428
  /**
351
429
  * Create a new command instance.
352
430
  *
@@ -356,6 +434,11 @@ var Command = class {
356
434
  this.serialize = defaultSerializer;
357
435
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
358
436
  this.command = command.map((c) => this.serialize(c));
437
+ this.headers = opts?.headers;
438
+ this.path = opts?.path;
439
+ this.onMessage = opts?.streamOptions?.onMessage;
440
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
441
+ this.signal = opts?.streamOptions?.signal;
359
442
  if (opts?.latencyLogging) {
360
443
  const originalExec = this.exec.bind(this);
361
444
  this.exec = async (client) => {
@@ -376,7 +459,12 @@ var Command = class {
376
459
  async exec(client) {
377
460
  const { result, error } = await client.request({
378
461
  body: this.command,
379
- upstashSyncToken: client.upstashSyncToken
462
+ path: this.path,
463
+ upstashSyncToken: client.upstashSyncToken,
464
+ headers: this.headers,
465
+ onMessage: this.onMessage,
466
+ isStreaming: this.isStreaming,
467
+ signal: this.signal
380
468
  });
381
469
  if (error) {
382
470
  throw new UpstashError(error);
@@ -504,6 +592,13 @@ var EchoCommand = class extends Command {
504
592
  }
505
593
  };
506
594
 
595
+ // pkg/commands/evalRo.ts
596
+ var EvalROCommand = class extends Command {
597
+ constructor([script, keys, args], opts) {
598
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
599
+ }
600
+ };
601
+
507
602
  // pkg/commands/eval.ts
508
603
  var EvalCommand = class extends Command {
509
604
  constructor([script, keys, args], opts) {
@@ -511,6 +606,13 @@ var EvalCommand = class extends Command {
511
606
  }
512
607
  };
513
608
 
609
+ // pkg/commands/evalshaRo.ts
610
+ var EvalshaROCommand = class extends Command {
611
+ constructor([sha, keys, args], opts) {
612
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
613
+ }
614
+ };
615
+
514
616
  // pkg/commands/evalsha.ts
515
617
  var EvalshaCommand = class extends Command {
516
618
  constructor([sha, keys, args], opts) {
@@ -518,6 +620,14 @@ var EvalshaCommand = class extends Command {
518
620
  }
519
621
  };
520
622
 
623
+ // pkg/commands/exec.ts
624
+ var ExecCommand = class extends Command {
625
+ constructor(cmd, opts) {
626
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
627
+ super(normalizedCmd, opts);
628
+ }
629
+ };
630
+
521
631
  // pkg/commands/exists.ts
522
632
  var ExistsCommand = class extends Command {
523
633
  constructor(cmd, opts) {
@@ -734,6 +844,27 @@ var GetDelCommand = class extends Command {
734
844
  }
735
845
  };
736
846
 
847
+ // pkg/commands/getex.ts
848
+ var GetExCommand = class extends Command {
849
+ constructor([key, opts], cmdOpts) {
850
+ const command = ["getex", key];
851
+ if (opts) {
852
+ if ("ex" in opts && typeof opts.ex === "number") {
853
+ command.push("ex", opts.ex);
854
+ } else if ("px" in opts && typeof opts.px === "number") {
855
+ command.push("px", opts.px);
856
+ } else if ("exat" in opts && typeof opts.exat === "number") {
857
+ command.push("exat", opts.exat);
858
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
859
+ command.push("pxat", opts.pxat);
860
+ } else if ("persist" in opts && opts.persist) {
861
+ command.push("persist");
862
+ }
863
+ }
864
+ super(command, cmdOpts);
865
+ }
866
+ };
867
+
737
868
  // pkg/commands/getrange.ts
738
869
  var GetRangeCommand = class extends Command {
739
870
  constructor(cmd, opts) {
@@ -762,6 +893,122 @@ var HExistsCommand = class extends Command {
762
893
  }
763
894
  };
764
895
 
896
+ // pkg/commands/hexpire.ts
897
+ var HExpireCommand = class extends Command {
898
+ constructor(cmd, opts) {
899
+ const [key, fields, seconds, option] = cmd;
900
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
901
+ super(
902
+ [
903
+ "hexpire",
904
+ key,
905
+ seconds,
906
+ ...option ? [option] : [],
907
+ "FIELDS",
908
+ fieldArray.length,
909
+ ...fieldArray
910
+ ],
911
+ opts
912
+ );
913
+ }
914
+ };
915
+
916
+ // pkg/commands/hexpireat.ts
917
+ var HExpireAtCommand = class extends Command {
918
+ constructor(cmd, opts) {
919
+ const [key, fields, timestamp, option] = cmd;
920
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
921
+ super(
922
+ [
923
+ "hexpireat",
924
+ key,
925
+ timestamp,
926
+ ...option ? [option] : [],
927
+ "FIELDS",
928
+ fieldArray.length,
929
+ ...fieldArray
930
+ ],
931
+ opts
932
+ );
933
+ }
934
+ };
935
+
936
+ // pkg/commands/hexpiretime.ts
937
+ var HExpireTimeCommand = class extends Command {
938
+ constructor(cmd, opts) {
939
+ const [key, fields] = cmd;
940
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
941
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
942
+ }
943
+ };
944
+
945
+ // pkg/commands/hpersist.ts
946
+ var HPersistCommand = class extends Command {
947
+ constructor(cmd, opts) {
948
+ const [key, fields] = cmd;
949
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
950
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
951
+ }
952
+ };
953
+
954
+ // pkg/commands/hpexpire.ts
955
+ var HPExpireCommand = class extends Command {
956
+ constructor(cmd, opts) {
957
+ const [key, fields, milliseconds, option] = cmd;
958
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
959
+ super(
960
+ [
961
+ "hpexpire",
962
+ key,
963
+ milliseconds,
964
+ ...option ? [option] : [],
965
+ "FIELDS",
966
+ fieldArray.length,
967
+ ...fieldArray
968
+ ],
969
+ opts
970
+ );
971
+ }
972
+ };
973
+
974
+ // pkg/commands/hpexpireat.ts
975
+ var HPExpireAtCommand = class extends Command {
976
+ constructor(cmd, opts) {
977
+ const [key, fields, timestamp, option] = cmd;
978
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
979
+ super(
980
+ [
981
+ "hpexpireat",
982
+ key,
983
+ timestamp,
984
+ ...option ? [option] : [],
985
+ "FIELDS",
986
+ fieldArray.length,
987
+ ...fieldArray
988
+ ],
989
+ opts
990
+ );
991
+ }
992
+ };
993
+
994
+ // pkg/commands/hpexpiretime.ts
995
+ var HPExpireTimeCommand = class extends Command {
996
+ constructor(cmd, opts) {
997
+ const [key, fields] = cmd;
998
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
999
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1000
+ }
1001
+ };
1002
+
1003
+ // pkg/commands/hpttl.ts
1004
+ var HPTtlCommand = class extends Command {
1005
+ constructor(cmd, opts) {
1006
+ const [key, fields] = cmd;
1007
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1008
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1009
+ }
1010
+ };
1011
+
765
1012
  // pkg/commands/hget.ts
766
1013
  var HGetCommand = class extends Command {
767
1014
  constructor(cmd, opts) {
@@ -775,9 +1022,9 @@ function deserialize(result) {
775
1022
  return null;
776
1023
  }
777
1024
  const obj = {};
778
- while (result.length >= 2) {
779
- const key = result.shift();
780
- const value = result.shift();
1025
+ for (let i = 0; i < result.length; i += 2) {
1026
+ const key = result[i];
1027
+ const value = result[i + 1];
781
1028
  try {
782
1029
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
783
1030
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -861,9 +1108,9 @@ function deserialize3(result) {
861
1108
  return null;
862
1109
  }
863
1110
  const obj = {};
864
- while (result.length >= 2) {
865
- const key = result.shift();
866
- const value = result.shift();
1111
+ for (let i = 0; i < result.length; i += 2) {
1112
+ const key = result[i];
1113
+ const value = result[i + 1];
867
1114
  try {
868
1115
  obj[key] = JSON.parse(value);
869
1116
  } catch {
@@ -927,6 +1174,15 @@ var HStrLenCommand = class extends Command {
927
1174
  }
928
1175
  };
929
1176
 
1177
+ // pkg/commands/httl.ts
1178
+ var HTtlCommand = class extends Command {
1179
+ constructor(cmd, opts) {
1180
+ const [key, fields] = cmd;
1181
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1182
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1183
+ }
1184
+ };
1185
+
930
1186
  // pkg/commands/hvals.ts
931
1187
  var HValsCommand = class extends Command {
932
1188
  constructor(cmd, opts) {
@@ -1046,6 +1302,14 @@ var JsonGetCommand = class extends Command {
1046
1302
  }
1047
1303
  };
1048
1304
 
1305
+ // pkg/commands/json_merge.ts
1306
+ var JsonMergeCommand = class extends Command {
1307
+ constructor(cmd, opts) {
1308
+ const command = ["JSON.MERGE", ...cmd];
1309
+ super(command, opts);
1310
+ }
1311
+ };
1312
+
1049
1313
  // pkg/commands/json_mget.ts
1050
1314
  var JsonMGetCommand = class extends Command {
1051
1315
  constructor(cmd, opts) {
@@ -1406,11 +1670,14 @@ var ScanCommand = class extends Command {
1406
1670
  if (typeof opts?.count === "number") {
1407
1671
  command.push("count", opts.count);
1408
1672
  }
1409
- if (opts?.type && opts.type.length > 0) {
1673
+ if (opts && "withType" in opts && opts.withType === true) {
1674
+ command.push("withtype");
1675
+ } else if (opts && "type" in opts && opts.type && opts.type.length > 0) {
1410
1676
  command.push("type", opts.type);
1411
1677
  }
1412
1678
  super(command, {
1413
- deserialize: deserializeScanResponse,
1679
+ // @ts-expect-error ignore types here
1680
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1414
1681
  ...cmdOpts
1415
1682
  });
1416
1683
  }
@@ -1836,15 +2103,15 @@ var XPendingCommand = class extends Command {
1836
2103
  function deserialize4(result) {
1837
2104
  const obj = {};
1838
2105
  for (const e of result) {
1839
- while (e.length >= 2) {
1840
- const streamId = e.shift();
1841
- const entries = e.shift();
2106
+ for (let i = 0; i < e.length; i += 2) {
2107
+ const streamId = e[i];
2108
+ const entries = e[i + 1];
1842
2109
  if (!(streamId in obj)) {
1843
2110
  obj[streamId] = {};
1844
2111
  }
1845
- while (entries.length >= 2) {
1846
- const field = entries.shift();
1847
- const value = entries.shift();
2112
+ for (let j = 0; j < entries.length; j += 2) {
2113
+ const field = entries[j];
2114
+ const value = entries[j + 1];
1848
2115
  try {
1849
2116
  obj[streamId][field] = JSON.parse(value);
1850
2117
  } catch {
@@ -1933,15 +2200,15 @@ var XRevRangeCommand = class extends Command {
1933
2200
  function deserialize5(result) {
1934
2201
  const obj = {};
1935
2202
  for (const e of result) {
1936
- while (e.length >= 2) {
1937
- const streamId = e.shift();
1938
- const entries = e.shift();
2203
+ for (let i = 0; i < e.length; i += 2) {
2204
+ const streamId = e[i];
2205
+ const entries = e[i + 1];
1939
2206
  if (!(streamId in obj)) {
1940
2207
  obj[streamId] = {};
1941
2208
  }
1942
- while (entries.length >= 2) {
1943
- const field = entries.shift();
1944
- const value = entries.shift();
2209
+ for (let j = 0; j < entries.length; j += 2) {
2210
+ const field = entries[j];
2211
+ const value = entries[j + 1];
1945
2212
  try {
1946
2213
  obj[streamId][field] = JSON.parse(value);
1947
2214
  } catch {
@@ -2200,6 +2467,183 @@ var ZUnionStoreCommand = class extends Command {
2200
2467
  }
2201
2468
  };
2202
2469
 
2470
+ // pkg/commands/psubscribe.ts
2471
+ var PSubscribeCommand = class extends Command {
2472
+ constructor(cmd, opts) {
2473
+ const sseHeaders = {
2474
+ Accept: "text/event-stream",
2475
+ "Cache-Control": "no-cache",
2476
+ Connection: "keep-alive"
2477
+ };
2478
+ super([], {
2479
+ ...opts,
2480
+ headers: sseHeaders,
2481
+ path: ["psubscribe", ...cmd],
2482
+ streamOptions: {
2483
+ isStreaming: true,
2484
+ onMessage: opts?.streamOptions?.onMessage,
2485
+ signal: opts?.streamOptions?.signal
2486
+ }
2487
+ });
2488
+ }
2489
+ };
2490
+
2491
+ // pkg/commands/subscribe.ts
2492
+ var Subscriber = class extends EventTarget {
2493
+ subscriptions;
2494
+ client;
2495
+ listeners;
2496
+ constructor(client, channels, isPattern = false) {
2497
+ super();
2498
+ this.client = client;
2499
+ this.subscriptions = /* @__PURE__ */ new Map();
2500
+ this.listeners = /* @__PURE__ */ new Map();
2501
+ for (const channel of channels) {
2502
+ if (isPattern) {
2503
+ this.subscribeToPattern(channel);
2504
+ } else {
2505
+ this.subscribeToChannel(channel);
2506
+ }
2507
+ }
2508
+ }
2509
+ subscribeToChannel(channel) {
2510
+ const controller = new AbortController();
2511
+ const command = new SubscribeCommand([channel], {
2512
+ streamOptions: {
2513
+ signal: controller.signal,
2514
+ onMessage: (data) => this.handleMessage(data, false)
2515
+ }
2516
+ });
2517
+ command.exec(this.client).catch((error) => {
2518
+ if (error.name !== "AbortError") {
2519
+ this.dispatchToListeners("error", error);
2520
+ }
2521
+ });
2522
+ this.subscriptions.set(channel, {
2523
+ command,
2524
+ controller,
2525
+ isPattern: false
2526
+ });
2527
+ }
2528
+ subscribeToPattern(pattern) {
2529
+ const controller = new AbortController();
2530
+ const command = new PSubscribeCommand([pattern], {
2531
+ streamOptions: {
2532
+ signal: controller.signal,
2533
+ onMessage: (data) => this.handleMessage(data, true)
2534
+ }
2535
+ });
2536
+ command.exec(this.client).catch((error) => {
2537
+ if (error.name !== "AbortError") {
2538
+ this.dispatchToListeners("error", error);
2539
+ }
2540
+ });
2541
+ this.subscriptions.set(pattern, {
2542
+ command,
2543
+ controller,
2544
+ isPattern: true
2545
+ });
2546
+ }
2547
+ handleMessage(data, isPattern) {
2548
+ const messageData = data.replace(/^data:\s*/, "");
2549
+ const firstCommaIndex = messageData.indexOf(",");
2550
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
2551
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
2552
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
2553
+ const type = messageData.slice(0, firstCommaIndex);
2554
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
2555
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2556
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
2557
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
2558
+ try {
2559
+ const message = JSON.parse(messageStr);
2560
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
2561
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
2562
+ } catch (error) {
2563
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2564
+ }
2565
+ } else {
2566
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2567
+ const messageStr = messageData.slice(secondCommaIndex + 1);
2568
+ try {
2569
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
2570
+ const count = Number.parseInt(messageStr);
2571
+ this.dispatchToListeners(type, count);
2572
+ } else {
2573
+ const message = JSON.parse(messageStr);
2574
+ this.dispatchToListeners(type, { channel, message });
2575
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
2576
+ }
2577
+ } catch (error) {
2578
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2579
+ }
2580
+ }
2581
+ }
2582
+ }
2583
+ dispatchToListeners(type, data) {
2584
+ const listeners = this.listeners.get(type);
2585
+ if (listeners) {
2586
+ for (const listener of listeners) {
2587
+ listener(data);
2588
+ }
2589
+ }
2590
+ }
2591
+ on(type, listener) {
2592
+ if (!this.listeners.has(type)) {
2593
+ this.listeners.set(type, /* @__PURE__ */ new Set());
2594
+ }
2595
+ this.listeners.get(type)?.add(listener);
2596
+ }
2597
+ removeAllListeners() {
2598
+ this.listeners.clear();
2599
+ }
2600
+ async unsubscribe(channels) {
2601
+ if (channels) {
2602
+ for (const channel of channels) {
2603
+ const subscription = this.subscriptions.get(channel);
2604
+ if (subscription) {
2605
+ try {
2606
+ subscription.controller.abort();
2607
+ } catch {
2608
+ }
2609
+ this.subscriptions.delete(channel);
2610
+ }
2611
+ }
2612
+ } else {
2613
+ for (const subscription of this.subscriptions.values()) {
2614
+ try {
2615
+ subscription.controller.abort();
2616
+ } catch {
2617
+ }
2618
+ }
2619
+ this.subscriptions.clear();
2620
+ this.removeAllListeners();
2621
+ }
2622
+ }
2623
+ getSubscribedChannels() {
2624
+ return [...this.subscriptions.keys()];
2625
+ }
2626
+ };
2627
+ var SubscribeCommand = class extends Command {
2628
+ constructor(cmd, opts) {
2629
+ const sseHeaders = {
2630
+ Accept: "text/event-stream",
2631
+ "Cache-Control": "no-cache",
2632
+ Connection: "keep-alive"
2633
+ };
2634
+ super([], {
2635
+ ...opts,
2636
+ headers: sseHeaders,
2637
+ path: ["subscribe", ...cmd],
2638
+ streamOptions: {
2639
+ isStreaming: true,
2640
+ onMessage: opts?.streamOptions?.onMessage,
2641
+ signal: opts?.streamOptions?.signal
2642
+ }
2643
+ });
2644
+ }
2645
+ };
2646
+
2203
2647
  // pkg/commands/zdiffstore.ts
2204
2648
  var ZDiffStoreCommand = class extends Command {
2205
2649
  constructor(cmd, opts) {
@@ -2228,9 +2672,9 @@ var Pipeline = class {
2228
2672
  this.multiExec = opts.multiExec ?? false;
2229
2673
  if (this.commandOptions?.latencyLogging) {
2230
2674
  const originalExec = this.exec.bind(this);
2231
- this.exec = async () => {
2675
+ this.exec = async (options) => {
2232
2676
  const start = performance.now();
2233
- const result = await originalExec();
2677
+ const result = await (options ? originalExec(options) : originalExec());
2234
2678
  const end = performance.now();
2235
2679
  const loggerResult = (end - start).toFixed(2);
2236
2680
  console.log(
@@ -2240,19 +2684,7 @@ var Pipeline = class {
2240
2684
  };
2241
2685
  }
2242
2686
  }
2243
- /**
2244
- * Send the pipeline request to upstash.
2245
- *
2246
- * Returns an array with the results of all pipelined commands.
2247
- *
2248
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2249
- * ```ts
2250
- * const p = redis.pipeline()
2251
- * p.get("key")
2252
- * const result = p.exec<[{ greeting: string }]>()
2253
- * ```
2254
- */
2255
- exec = async () => {
2687
+ exec = async (options) => {
2256
2688
  if (this.commands.length === 0) {
2257
2689
  throw new Error("Pipeline is empty");
2258
2690
  }
@@ -2261,7 +2693,12 @@ var Pipeline = class {
2261
2693
  path,
2262
2694
  body: Object.values(this.commands).map((c) => c.command)
2263
2695
  });
2264
- return res.map(({ error, result }, i) => {
2696
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2697
+ return {
2698
+ error,
2699
+ result: this.commands[i].deserialize(result)
2700
+ };
2701
+ }) : res.map(({ error, result }, i) => {
2265
2702
  if (error) {
2266
2703
  throw new UpstashError(
2267
2704
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2347,10 +2784,18 @@ var Pipeline = class {
2347
2784
  * @see https://redis.io/commands/echo
2348
2785
  */
2349
2786
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2787
+ /**
2788
+ * @see https://redis.io/commands/eval_ro
2789
+ */
2790
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2350
2791
  /**
2351
2792
  * @see https://redis.io/commands/eval
2352
2793
  */
2353
2794
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2795
+ /**
2796
+ * @see https://redis.io/commands/evalsha_ro
2797
+ */
2798
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2354
2799
  /**
2355
2800
  * @see https://redis.io/commands/evalsha
2356
2801
  */
@@ -2411,6 +2856,10 @@ var Pipeline = class {
2411
2856
  * @see https://redis.io/commands/getdel
2412
2857
  */
2413
2858
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2859
+ /**
2860
+ * @see https://redis.io/commands/getex
2861
+ */
2862
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2414
2863
  /**
2415
2864
  * @see https://redis.io/commands/getrange
2416
2865
  */
@@ -2427,6 +2876,42 @@ var Pipeline = class {
2427
2876
  * @see https://redis.io/commands/hexists
2428
2877
  */
2429
2878
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2879
+ /**
2880
+ * @see https://redis.io/commands/hexpire
2881
+ */
2882
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2883
+ /**
2884
+ * @see https://redis.io/commands/hexpireat
2885
+ */
2886
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2887
+ /**
2888
+ * @see https://redis.io/commands/hexpiretime
2889
+ */
2890
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2891
+ /**
2892
+ * @see https://redis.io/commands/httl
2893
+ */
2894
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2895
+ /**
2896
+ * @see https://redis.io/commands/hpexpire
2897
+ */
2898
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2899
+ /**
2900
+ * @see https://redis.io/commands/hpexpireat
2901
+ */
2902
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2903
+ /**
2904
+ * @see https://redis.io/commands/hpexpiretime
2905
+ */
2906
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2907
+ /**
2908
+ * @see https://redis.io/commands/hpttl
2909
+ */
2910
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2911
+ /**
2912
+ * @see https://redis.io/commands/hpersist
2913
+ */
2914
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2430
2915
  /**
2431
2916
  * @see https://redis.io/commands/hget
2432
2917
  */
@@ -2944,6 +3429,10 @@ var Pipeline = class {
2944
3429
  * @see https://redis.io/commands/json.get
2945
3430
  */
2946
3431
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3432
+ /**
3433
+ * @see https://redis.io/commands/json.merge
3434
+ */
3435
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2947
3436
  /**
2948
3437
  * @see https://redis.io/commands/json.mget
2949
3438
  */
@@ -2997,27 +3486,43 @@ var Pipeline = class {
2997
3486
  };
2998
3487
 
2999
3488
  // pkg/script.ts
3000
- var import_enc_hex = __toESM(require("crypto-js/enc-hex.js"));
3001
- var import_sha1 = __toESM(require("crypto-js/sha1.js"));
3489
+ var import_uncrypto = require("uncrypto");
3002
3490
  var Script = class {
3003
3491
  script;
3492
+ /**
3493
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3494
+ * asynchronously. Do not use this property immidiately after the constructor.
3495
+ *
3496
+ * This property is only exposed for backwards compatibility and will be removed in the
3497
+ * future major release.
3498
+ */
3004
3499
  sha1;
3005
3500
  redis;
3006
3501
  constructor(redis, script) {
3007
3502
  this.redis = redis;
3008
- this.sha1 = this.digest(script);
3009
3503
  this.script = script;
3504
+ this.sha1 = "";
3505
+ void this.init(script);
3506
+ }
3507
+ /**
3508
+ * Initialize the script by computing its SHA-1 hash.
3509
+ */
3510
+ async init(script) {
3511
+ if (this.sha1) return;
3512
+ this.sha1 = await this.digest(script);
3010
3513
  }
3011
3514
  /**
3012
3515
  * Send an `EVAL` command to redis.
3013
3516
  */
3014
3517
  async eval(keys, args) {
3518
+ await this.init(this.script);
3015
3519
  return await this.redis.eval(this.script, keys, args);
3016
3520
  }
3017
3521
  /**
3018
3522
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
3019
3523
  */
3020
3524
  async evalsha(keys, args) {
3525
+ await this.init(this.script);
3021
3526
  return await this.redis.evalsha(this.sha1, keys, args);
3022
3527
  }
3023
3528
  /**
@@ -3027,6 +3532,7 @@ var Script = class {
3027
3532
  * Following calls will be able to use the cached script
3028
3533
  */
3029
3534
  async exec(keys, args) {
3535
+ await this.init(this.script);
3030
3536
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
3031
3537
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3032
3538
  return await this.redis.eval(this.script, keys, args);
@@ -3038,8 +3544,75 @@ var Script = class {
3038
3544
  /**
3039
3545
  * Compute the sha1 hash of the script and return its hex representation.
3040
3546
  */
3041
- digest(s) {
3042
- return import_enc_hex.default.stringify((0, import_sha1.default)(s));
3547
+ async digest(s) {
3548
+ const data = new TextEncoder().encode(s);
3549
+ const hashBuffer = await import_uncrypto.subtle.digest("SHA-1", data);
3550
+ const hashArray = [...new Uint8Array(hashBuffer)];
3551
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3552
+ }
3553
+ };
3554
+
3555
+ // pkg/scriptRo.ts
3556
+ var import_uncrypto2 = require("uncrypto");
3557
+ var ScriptRO = class {
3558
+ script;
3559
+ /**
3560
+ * @deprecated This property is initialized to an empty string and will be set in the init method
3561
+ * asynchronously. Do not use this property immidiately after the constructor.
3562
+ *
3563
+ * This property is only exposed for backwards compatibility and will be removed in the
3564
+ * future major release.
3565
+ */
3566
+ sha1;
3567
+ redis;
3568
+ constructor(redis, script) {
3569
+ this.redis = redis;
3570
+ this.sha1 = "";
3571
+ this.script = script;
3572
+ void this.init(script);
3573
+ }
3574
+ async init(script) {
3575
+ if (this.sha1) return;
3576
+ this.sha1 = await this.digest(script);
3577
+ }
3578
+ /**
3579
+ * Send an `EVAL_RO` command to redis.
3580
+ */
3581
+ async evalRo(keys, args) {
3582
+ await this.init(this.script);
3583
+ return await this.redis.evalRo(this.script, keys, args);
3584
+ }
3585
+ /**
3586
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3587
+ */
3588
+ async evalshaRo(keys, args) {
3589
+ await this.init(this.script);
3590
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3591
+ }
3592
+ /**
3593
+ * Optimistically try to run `EVALSHA_RO` first.
3594
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3595
+ *
3596
+ * Following calls will be able to use the cached script
3597
+ */
3598
+ async exec(keys, args) {
3599
+ await this.init(this.script);
3600
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3601
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3602
+ return await this.redis.evalRo(this.script, keys, args);
3603
+ }
3604
+ throw error;
3605
+ });
3606
+ return res;
3607
+ }
3608
+ /**
3609
+ * Compute the sha1 hash of the script and return its hex representation.
3610
+ */
3611
+ async digest(s) {
3612
+ const data = new TextEncoder().encode(s);
3613
+ const hashBuffer = await import_uncrypto2.subtle.digest("SHA-1", data);
3614
+ const hashArray = [...new Uint8Array(hashBuffer)];
3615
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
3043
3616
  }
3044
3617
  };
3045
3618
 
@@ -3117,6 +3690,10 @@ var Redis = class {
3117
3690
  * @see https://redis.io/commands/json.get
3118
3691
  */
3119
3692
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3693
+ /**
3694
+ * @see https://redis.io/commands/json.merge
3695
+ */
3696
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3120
3697
  /**
3121
3698
  * @see https://redis.io/commands/json.mget
3122
3699
  */
@@ -3186,8 +3763,36 @@ var Redis = class {
3186
3763
  } catch {
3187
3764
  }
3188
3765
  };
3189
- createScript(script) {
3190
- return new Script(this, script);
3766
+ /**
3767
+ * Creates a new script.
3768
+ *
3769
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3770
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3771
+ * the entire script. Afterwards, the script is cached on the server.
3772
+ *
3773
+ * @param script - The script to create
3774
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3775
+ * @returns A new script
3776
+ *
3777
+ * @example
3778
+ * ```ts
3779
+ * const redis = new Redis({...})
3780
+ *
3781
+ * const script = redis.createScript<string>("return ARGV[1];")
3782
+ * const arg1 = await script.eval([], ["Hello World"])
3783
+ * expect(arg1, "Hello World")
3784
+ * ```
3785
+ * @example
3786
+ * ```ts
3787
+ * const redis = new Redis({...})
3788
+ *
3789
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3790
+ * const arg1 = await script.evalRo([], ["Hello World"])
3791
+ * expect(arg1, "Hello World")
3792
+ * ```
3793
+ */
3794
+ createScript(script, opts) {
3795
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3191
3796
  }
3192
3797
  /**
3193
3798
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3274,14 +3879,26 @@ var Redis = class {
3274
3879
  * @see https://redis.io/commands/echo
3275
3880
  */
3276
3881
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3882
+ /**
3883
+ * @see https://redis.io/commands/eval_ro
3884
+ */
3885
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3277
3886
  /**
3278
3887
  * @see https://redis.io/commands/eval
3279
3888
  */
3280
3889
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3890
+ /**
3891
+ * @see https://redis.io/commands/evalsha_ro
3892
+ */
3893
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3281
3894
  /**
3282
3895
  * @see https://redis.io/commands/evalsha
3283
3896
  */
3284
3897
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3898
+ /**
3899
+ * Generic method to execute any Redis command.
3900
+ */
3901
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3285
3902
  /**
3286
3903
  * @see https://redis.io/commands/exists
3287
3904
  */
@@ -3338,6 +3955,10 @@ var Redis = class {
3338
3955
  * @see https://redis.io/commands/getdel
3339
3956
  */
3340
3957
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3958
+ /**
3959
+ * @see https://redis.io/commands/getex
3960
+ */
3961
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3341
3962
  /**
3342
3963
  * @see https://redis.io/commands/getrange
3343
3964
  */
@@ -3354,6 +3975,42 @@ var Redis = class {
3354
3975
  * @see https://redis.io/commands/hexists
3355
3976
  */
3356
3977
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3978
+ /**
3979
+ * @see https://redis.io/commands/hexpire
3980
+ */
3981
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3982
+ /**
3983
+ * @see https://redis.io/commands/hexpireat
3984
+ */
3985
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3986
+ /**
3987
+ * @see https://redis.io/commands/hexpiretime
3988
+ */
3989
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3990
+ /**
3991
+ * @see https://redis.io/commands/httl
3992
+ */
3993
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3994
+ /**
3995
+ * @see https://redis.io/commands/hpexpire
3996
+ */
3997
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3998
+ /**
3999
+ * @see https://redis.io/commands/hpexpireat
4000
+ */
4001
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
4002
+ /**
4003
+ * @see https://redis.io/commands/hpexpiretime
4004
+ */
4005
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
4006
+ /**
4007
+ * @see https://redis.io/commands/hpttl
4008
+ */
4009
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
4010
+ /**
4011
+ * @see https://redis.io/commands/hpersist
4012
+ */
4013
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3357
4014
  /**
3358
4015
  * @see https://redis.io/commands/hget
3359
4016
  */
@@ -3522,6 +4179,13 @@ var Redis = class {
3522
4179
  * @see https://redis.io/commands/psetex
3523
4180
  */
3524
4181
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4182
+ /**
4183
+ * @see https://redis.io/commands/psubscribe
4184
+ */
4185
+ psubscribe = (patterns) => {
4186
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4187
+ return new Subscriber(this.client, patternArray, true);
4188
+ };
3525
4189
  /**
3526
4190
  * @see https://redis.io/commands/pttl
3527
4191
  */
@@ -3558,10 +4222,9 @@ var Redis = class {
3558
4222
  * @see https://redis.io/commands/sadd
3559
4223
  */
3560
4224
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3561
- /**
3562
- * @see https://redis.io/commands/scan
3563
- */
3564
- scan = (...args) => new ScanCommand(args, this.opts).exec(this.client);
4225
+ scan(cursor, opts) {
4226
+ return new ScanCommand([cursor, opts], this.opts).exec(this.client);
4227
+ }
3565
4228
  /**
3566
4229
  * @see https://redis.io/commands/scard
3567
4230
  */
@@ -3650,6 +4313,13 @@ var Redis = class {
3650
4313
  * @see https://redis.io/commands/strlen
3651
4314
  */
3652
4315
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4316
+ /**
4317
+ * @see https://redis.io/commands/subscribe
4318
+ */
4319
+ subscribe = (channels) => {
4320
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4321
+ return new Subscriber(this.client, channelArray);
4322
+ };
3653
4323
  /**
3654
4324
  * @see https://redis.io/commands/sunion
3655
4325
  */
@@ -3851,18 +4521,16 @@ var Redis2 = class _Redis extends Redis {
3851
4521
  console.warn(
3852
4522
  `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.`
3853
4523
  );
3854
- }
3855
- if (!config.token) {
4524
+ } else if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
3856
4525
  console.warn(
3857
- `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
4526
+ "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
3858
4527
  );
3859
4528
  }
3860
- if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
4529
+ if (!config.token) {
3861
4530
  console.warn(
3862
- "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
4531
+ `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
3863
4532
  );
3864
- }
3865
- if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
4533
+ } else if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
3866
4534
  console.warn(
3867
4535
  "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!"
3868
4536
  );