@upstash/redis 0.0.0-ci.ff7a0b135b28ff50a7e2634a78123684be3ed71f-20241105152627 → 0.0.0-ci.ff91bb10adb82f9d009471b11db70c40b6dbf5f8-20251205205412

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,11 +8,12 @@ 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
  };
@@ -24,6 +25,58 @@ var UrlError = class extends Error {
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 {
@@ -73,15 +126,20 @@ var HttpClient = class {
73
126
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
74
127
  }
75
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";
76
134
  const requestOptions = {
77
135
  //@ts-expect-error this should throw due to bun regression
78
136
  cache: this.options.cache,
79
137
  method: "POST",
80
- headers: this.headers,
138
+ headers: requestHeaders,
81
139
  body: JSON.stringify(req.body),
82
140
  keepalive: this.options.keepAlive,
83
141
  agent: this.options.agent,
84
- signal: this.options.signal,
142
+ signal: isSignalFunction ? signal() : signal,
85
143
  /**
86
144
  * Fastly specific
87
145
  */
@@ -100,16 +158,18 @@ var HttpClient = class {
100
158
  let error = null;
101
159
  for (let i = 0; i <= this.retry.attempts; i++) {
102
160
  try {
103
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
161
+ res = await fetch(requestUrl, requestOptions);
104
162
  break;
105
163
  } catch (error_) {
106
- if (this.options.signal?.aborted) {
164
+ if (requestOptions.signal?.aborted && isSignalFunction) {
165
+ throw error_;
166
+ } else if (requestOptions.signal?.aborted) {
107
167
  const myBlob = new Blob([
108
- JSON.stringify({ result: this.options.signal.reason ?? "Aborted" })
168
+ JSON.stringify({ result: requestOptions.signal.reason ?? "Aborted" })
109
169
  ]);
110
170
  const myOptions = {
111
171
  status: 200,
112
- statusText: this.options.signal.reason ?? "Aborted"
172
+ statusText: requestOptions.signal.reason ?? "Aborted"
113
173
  };
114
174
  res = new Response(myBlob, myOptions);
115
175
  break;
@@ -123,14 +183,58 @@ var HttpClient = class {
123
183
  if (!res) {
124
184
  throw error ?? new Error("Exhausted all retries");
125
185
  }
126
- const body = await res.json();
127
186
  if (!res.ok) {
128
- 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)}`);
129
195
  }
130
196
  if (this.readYourWrites) {
131
197
  const headers = res.headers;
132
198
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
133
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
+ }
134
238
  if (this.readYourWrites) {
135
239
  const headers = res.headers;
136
240
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -201,31 +305,6 @@ function merge(obj, key, value) {
201
305
  return obj;
202
306
  }
203
307
 
204
- // pkg/util.ts
205
- function parseRecursive(obj) {
206
- const parsed = Array.isArray(obj) ? obj.map((o) => {
207
- try {
208
- return parseRecursive(o);
209
- } catch {
210
- return o;
211
- }
212
- }) : JSON.parse(obj);
213
- if (typeof parsed === "number" && parsed.toString() !== obj) {
214
- return obj;
215
- }
216
- return parsed;
217
- }
218
- function parseResponse(result) {
219
- try {
220
- return parseRecursive(result);
221
- } catch {
222
- return result;
223
- }
224
- }
225
- function deserializeScanResponse(result) {
226
- return [result[0], ...parseResponse(result.slice(1))];
227
- }
228
-
229
308
  // pkg/commands/command.ts
230
309
  var defaultSerializer = (c) => {
231
310
  switch (typeof c) {
@@ -243,6 +322,11 @@ var Command = class {
243
322
  command;
244
323
  serialize;
245
324
  deserialize;
325
+ headers;
326
+ path;
327
+ onMessage;
328
+ isStreaming;
329
+ signal;
246
330
  /**
247
331
  * Create a new command instance.
248
332
  *
@@ -252,6 +336,11 @@ var Command = class {
252
336
  this.serialize = defaultSerializer;
253
337
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
254
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;
255
344
  if (opts?.latencyLogging) {
256
345
  const originalExec = this.exec.bind(this);
257
346
  this.exec = async (client) => {
@@ -272,7 +361,12 @@ var Command = class {
272
361
  async exec(client) {
273
362
  const { result, error } = await client.request({
274
363
  body: this.command,
275
- 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
276
370
  });
277
371
  if (error) {
278
372
  throw new UpstashError(error);
@@ -290,9 +384,9 @@ function deserialize(result) {
290
384
  return null;
291
385
  }
292
386
  const obj = {};
293
- while (result.length >= 2) {
294
- const key = result.shift();
295
- 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];
296
390
  try {
297
391
  obj[key] = JSON.parse(value);
298
392
  } catch {
@@ -434,6 +528,13 @@ var EchoCommand = class extends Command {
434
528
  }
435
529
  };
436
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
+
437
538
  // pkg/commands/eval.ts
438
539
  var EvalCommand = class extends Command {
439
540
  constructor([script, keys, args], opts) {
@@ -441,6 +542,13 @@ var EvalCommand = class extends Command {
441
542
  }
442
543
  };
443
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
+
444
552
  // pkg/commands/evalsha.ts
445
553
  var EvalshaCommand = class extends Command {
446
554
  constructor([sha, keys, args], opts) {
@@ -448,6 +556,14 @@ var EvalshaCommand = class extends Command {
448
556
  }
449
557
  };
450
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
+
451
567
  // pkg/commands/exists.ts
452
568
  var ExistsCommand = class extends Command {
453
569
  constructor(cmd, opts) {
@@ -664,6 +780,27 @@ var GetDelCommand = class extends Command {
664
780
  }
665
781
  };
666
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
+
667
804
  // pkg/commands/getrange.ts
668
805
  var GetRangeCommand = class extends Command {
669
806
  constructor(cmd, opts) {
@@ -692,6 +829,122 @@ var HExistsCommand = class extends Command {
692
829
  }
693
830
  };
694
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
+
695
948
  // pkg/commands/hget.ts
696
949
  var HGetCommand = class extends Command {
697
950
  constructor(cmd, opts) {
@@ -705,9 +958,9 @@ function deserialize2(result) {
705
958
  return null;
706
959
  }
707
960
  const obj = {};
708
- while (result.length >= 2) {
709
- const key = result.shift();
710
- 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];
711
964
  try {
712
965
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
713
966
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -823,6 +1076,15 @@ var HStrLenCommand = class extends Command {
823
1076
  }
824
1077
  };
825
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
+
826
1088
  // pkg/commands/hvals.ts
827
1089
  var HValsCommand = class extends Command {
828
1090
  constructor(cmd, opts) {
@@ -942,6 +1204,14 @@ var JsonGetCommand = class extends Command {
942
1204
  }
943
1205
  };
944
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
+
945
1215
  // pkg/commands/json_mget.ts
946
1216
  var JsonMGetCommand = class extends Command {
947
1217
  constructor(cmd, opts) {
@@ -1302,11 +1572,14 @@ var ScanCommand = class extends Command {
1302
1572
  if (typeof opts?.count === "number") {
1303
1573
  command.push("count", opts.count);
1304
1574
  }
1305
- 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) {
1306
1578
  command.push("type", opts.type);
1307
1579
  }
1308
1580
  super(command, {
1309
- deserialize: deserializeScanResponse,
1581
+ // @ts-expect-error ignore types here
1582
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1310
1583
  ...cmdOpts
1311
1584
  });
1312
1585
  }
@@ -1732,15 +2005,15 @@ var XPendingCommand = class extends Command {
1732
2005
  function deserialize4(result) {
1733
2006
  const obj = {};
1734
2007
  for (const e of result) {
1735
- while (e.length >= 2) {
1736
- const streamId = e.shift();
1737
- 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];
1738
2011
  if (!(streamId in obj)) {
1739
2012
  obj[streamId] = {};
1740
2013
  }
1741
- while (entries.length >= 2) {
1742
- const field = entries.shift();
1743
- 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];
1744
2017
  try {
1745
2018
  obj[streamId][field] = JSON.parse(value);
1746
2019
  } catch {
@@ -1829,15 +2102,15 @@ var XRevRangeCommand = class extends Command {
1829
2102
  function deserialize5(result) {
1830
2103
  const obj = {};
1831
2104
  for (const e of result) {
1832
- while (e.length >= 2) {
1833
- const streamId = e.shift();
1834
- 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];
1835
2108
  if (!(streamId in obj)) {
1836
2109
  obj[streamId] = {};
1837
2110
  }
1838
- while (entries.length >= 2) {
1839
- const field = entries.shift();
1840
- 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];
1841
2114
  try {
1842
2115
  obj[streamId][field] = JSON.parse(value);
1843
2116
  } catch {
@@ -2236,10 +2509,18 @@ var Pipeline = class {
2236
2509
  * @see https://redis.io/commands/echo
2237
2510
  */
2238
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));
2239
2516
  /**
2240
2517
  * @see https://redis.io/commands/eval
2241
2518
  */
2242
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));
2243
2524
  /**
2244
2525
  * @see https://redis.io/commands/evalsha
2245
2526
  */
@@ -2300,6 +2581,10 @@ var Pipeline = class {
2300
2581
  * @see https://redis.io/commands/getdel
2301
2582
  */
2302
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));
2303
2588
  /**
2304
2589
  * @see https://redis.io/commands/getrange
2305
2590
  */
@@ -2316,6 +2601,42 @@ var Pipeline = class {
2316
2601
  * @see https://redis.io/commands/hexists
2317
2602
  */
2318
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));
2319
2640
  /**
2320
2641
  * @see https://redis.io/commands/hget
2321
2642
  */
@@ -2833,6 +3154,10 @@ var Pipeline = class {
2833
3154
  * @see https://redis.io/commands/json.get
2834
3155
  */
2835
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)),
2836
3161
  /**
2837
3162
  * @see https://redis.io/commands/json.mget
2838
3163
  */
@@ -2886,6 +3211,24 @@ var Pipeline = class {
2886
3211
  };
2887
3212
 
2888
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
+ ]);
2889
3232
  function createAutoPipelineProxy(_redis, json) {
2890
3233
  const redis = _redis;
2891
3234
  if (!redis.autoPipelineExecutor) {
@@ -2900,7 +3243,8 @@ function createAutoPipelineProxy(_redis, json) {
2900
3243
  return createAutoPipelineProxy(redis2, true);
2901
3244
  }
2902
3245
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2903
- if (commandInRedisButNotPipeline) {
3246
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3247
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2904
3248
  return redis2[command];
2905
3249
  }
2906
3250
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2964,28 +3308,230 @@ var AutoPipelineExecutor = class {
2964
3308
  }
2965
3309
  };
2966
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
+
2967
3497
  // pkg/script.ts
2968
- import Hex from "crypto-js/enc-hex.js";
2969
- import sha1 from "crypto-js/sha1.js";
3498
+ import { subtle } from "uncrypto";
2970
3499
  var Script = class {
2971
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
+ */
2972
3508
  sha1;
2973
3509
  redis;
2974
3510
  constructor(redis, script) {
2975
3511
  this.redis = redis;
2976
- this.sha1 = this.digest(script);
2977
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);
2978
3522
  }
2979
3523
  /**
2980
3524
  * Send an `EVAL` command to redis.
2981
3525
  */
2982
3526
  async eval(keys, args) {
3527
+ await this.init(this.script);
2983
3528
  return await this.redis.eval(this.script, keys, args);
2984
3529
  }
2985
3530
  /**
2986
3531
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
2987
3532
  */
2988
3533
  async evalsha(keys, args) {
3534
+ await this.init(this.script);
2989
3535
  return await this.redis.evalsha(this.sha1, keys, args);
2990
3536
  }
2991
3537
  /**
@@ -2995,6 +3541,7 @@ var Script = class {
2995
3541
  * Following calls will be able to use the cached script
2996
3542
  */
2997
3543
  async exec(keys, args) {
3544
+ await this.init(this.script);
2998
3545
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
2999
3546
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3000
3547
  return await this.redis.eval(this.script, keys, args);
@@ -3006,8 +3553,75 @@ var Script = class {
3006
3553
  /**
3007
3554
  * Compute the sha1 hash of the script and return its hex representation.
3008
3555
  */
3009
- digest(s) {
3010
- 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("");
3011
3625
  }
3012
3626
  };
3013
3627
 
@@ -3085,6 +3699,10 @@ var Redis = class {
3085
3699
  * @see https://redis.io/commands/json.get
3086
3700
  */
3087
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),
3088
3706
  /**
3089
3707
  * @see https://redis.io/commands/json.mget
3090
3708
  */
@@ -3154,8 +3772,36 @@ var Redis = class {
3154
3772
  } catch {
3155
3773
  }
3156
3774
  };
3157
- createScript(script) {
3158
- 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);
3159
3805
  }
3160
3806
  /**
3161
3807
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3242,14 +3888,26 @@ var Redis = class {
3242
3888
  * @see https://redis.io/commands/echo
3243
3889
  */
3244
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);
3245
3895
  /**
3246
3896
  * @see https://redis.io/commands/eval
3247
3897
  */
3248
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);
3249
3903
  /**
3250
3904
  * @see https://redis.io/commands/evalsha
3251
3905
  */
3252
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);
3253
3911
  /**
3254
3912
  * @see https://redis.io/commands/exists
3255
3913
  */
@@ -3306,6 +3964,10 @@ var Redis = class {
3306
3964
  * @see https://redis.io/commands/getdel
3307
3965
  */
3308
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);
3309
3971
  /**
3310
3972
  * @see https://redis.io/commands/getrange
3311
3973
  */
@@ -3322,6 +3984,42 @@ var Redis = class {
3322
3984
  * @see https://redis.io/commands/hexists
3323
3985
  */
3324
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);
3325
4023
  /**
3326
4024
  * @see https://redis.io/commands/hget
3327
4025
  */
@@ -3490,6 +4188,13 @@ var Redis = class {
3490
4188
  * @see https://redis.io/commands/psetex
3491
4189
  */
3492
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
+ };
3493
4198
  /**
3494
4199
  * @see https://redis.io/commands/pttl
3495
4200
  */
@@ -3526,10 +4231,9 @@ var Redis = class {
3526
4231
  * @see https://redis.io/commands/sadd
3527
4232
  */
3528
4233
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3529
- /**
3530
- * @see https://redis.io/commands/scan
3531
- */
3532
- 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
+ }
3533
4237
  /**
3534
4238
  * @see https://redis.io/commands/scard
3535
4239
  */
@@ -3618,6 +4322,13 @@ var Redis = class {
3618
4322
  * @see https://redis.io/commands/strlen
3619
4323
  */
3620
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
+ };
3621
4332
  /**
3622
4333
  * @see https://redis.io/commands/sunion
3623
4334
  */