@upstash/redis 0.0.0-ci.fe7ec0544f213f3f58bb19e23dfafafab828c00c-20241107145222 → 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) {
@@ -672,6 +780,27 @@ var GetDelCommand = class extends Command {
672
780
  }
673
781
  };
674
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
+
675
804
  // pkg/commands/getrange.ts
676
805
  var GetRangeCommand = class extends Command {
677
806
  constructor(cmd, opts) {
@@ -700,6 +829,122 @@ var HExistsCommand = class extends Command {
700
829
  }
701
830
  };
702
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
+
703
948
  // pkg/commands/hget.ts
704
949
  var HGetCommand = class extends Command {
705
950
  constructor(cmd, opts) {
@@ -713,9 +958,9 @@ function deserialize2(result) {
713
958
  return null;
714
959
  }
715
960
  const obj = {};
716
- while (result.length >= 2) {
717
- const key = result.shift();
718
- 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];
719
964
  try {
720
965
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
721
966
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -831,6 +1076,15 @@ var HStrLenCommand = class extends Command {
831
1076
  }
832
1077
  };
833
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
+
834
1088
  // pkg/commands/hvals.ts
835
1089
  var HValsCommand = class extends Command {
836
1090
  constructor(cmd, opts) {
@@ -950,6 +1204,14 @@ var JsonGetCommand = class extends Command {
950
1204
  }
951
1205
  };
952
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
+
953
1215
  // pkg/commands/json_mget.ts
954
1216
  var JsonMGetCommand = class extends Command {
955
1217
  constructor(cmd, opts) {
@@ -1310,11 +1572,14 @@ var ScanCommand = class extends Command {
1310
1572
  if (typeof opts?.count === "number") {
1311
1573
  command.push("count", opts.count);
1312
1574
  }
1313
- 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) {
1314
1578
  command.push("type", opts.type);
1315
1579
  }
1316
1580
  super(command, {
1317
- deserialize: deserializeScanResponse,
1581
+ // @ts-expect-error ignore types here
1582
+ deserialize: opts?.withType ? deserializeScanWithTypesResponse : deserializeScanResponse,
1318
1583
  ...cmdOpts
1319
1584
  });
1320
1585
  }
@@ -1740,15 +2005,15 @@ var XPendingCommand = class extends Command {
1740
2005
  function deserialize4(result) {
1741
2006
  const obj = {};
1742
2007
  for (const e of result) {
1743
- while (e.length >= 2) {
1744
- const streamId = e.shift();
1745
- 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];
1746
2011
  if (!(streamId in obj)) {
1747
2012
  obj[streamId] = {};
1748
2013
  }
1749
- while (entries.length >= 2) {
1750
- const field = entries.shift();
1751
- 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];
1752
2017
  try {
1753
2018
  obj[streamId][field] = JSON.parse(value);
1754
2019
  } catch {
@@ -1837,15 +2102,15 @@ var XRevRangeCommand = class extends Command {
1837
2102
  function deserialize5(result) {
1838
2103
  const obj = {};
1839
2104
  for (const e of result) {
1840
- while (e.length >= 2) {
1841
- const streamId = e.shift();
1842
- 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];
1843
2108
  if (!(streamId in obj)) {
1844
2109
  obj[streamId] = {};
1845
2110
  }
1846
- while (entries.length >= 2) {
1847
- const field = entries.shift();
1848
- 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];
1849
2114
  try {
1850
2115
  obj[streamId][field] = JSON.parse(value);
1851
2116
  } catch {
@@ -2244,10 +2509,18 @@ var Pipeline = class {
2244
2509
  * @see https://redis.io/commands/echo
2245
2510
  */
2246
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));
2247
2516
  /**
2248
2517
  * @see https://redis.io/commands/eval
2249
2518
  */
2250
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));
2251
2524
  /**
2252
2525
  * @see https://redis.io/commands/evalsha
2253
2526
  */
@@ -2308,6 +2581,10 @@ var Pipeline = class {
2308
2581
  * @see https://redis.io/commands/getdel
2309
2582
  */
2310
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));
2311
2588
  /**
2312
2589
  * @see https://redis.io/commands/getrange
2313
2590
  */
@@ -2324,6 +2601,42 @@ var Pipeline = class {
2324
2601
  * @see https://redis.io/commands/hexists
2325
2602
  */
2326
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));
2327
2640
  /**
2328
2641
  * @see https://redis.io/commands/hget
2329
2642
  */
@@ -2841,6 +3154,10 @@ var Pipeline = class {
2841
3154
  * @see https://redis.io/commands/json.get
2842
3155
  */
2843
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)),
2844
3161
  /**
2845
3162
  * @see https://redis.io/commands/json.mget
2846
3163
  */
@@ -2894,6 +3211,24 @@ var Pipeline = class {
2894
3211
  };
2895
3212
 
2896
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
+ ]);
2897
3232
  function createAutoPipelineProxy(_redis, json) {
2898
3233
  const redis = _redis;
2899
3234
  if (!redis.autoPipelineExecutor) {
@@ -2908,7 +3243,8 @@ function createAutoPipelineProxy(_redis, json) {
2908
3243
  return createAutoPipelineProxy(redis2, true);
2909
3244
  }
2910
3245
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2911
- if (commandInRedisButNotPipeline) {
3246
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3247
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2912
3248
  return redis2[command];
2913
3249
  }
2914
3250
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2972,28 +3308,230 @@ var AutoPipelineExecutor = class {
2972
3308
  }
2973
3309
  };
2974
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
+
2975
3497
  // pkg/script.ts
2976
- import Hex from "crypto-js/enc-hex.js";
2977
- import sha1 from "crypto-js/sha1.js";
3498
+ import { subtle } from "uncrypto";
2978
3499
  var Script = class {
2979
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
+ */
2980
3508
  sha1;
2981
3509
  redis;
2982
3510
  constructor(redis, script) {
2983
3511
  this.redis = redis;
2984
- this.sha1 = this.digest(script);
2985
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);
2986
3522
  }
2987
3523
  /**
2988
3524
  * Send an `EVAL` command to redis.
2989
3525
  */
2990
3526
  async eval(keys, args) {
3527
+ await this.init(this.script);
2991
3528
  return await this.redis.eval(this.script, keys, args);
2992
3529
  }
2993
3530
  /**
2994
3531
  * Calculates the sha1 hash of the script and then calls `EVALSHA`.
2995
3532
  */
2996
3533
  async evalsha(keys, args) {
3534
+ await this.init(this.script);
2997
3535
  return await this.redis.evalsha(this.sha1, keys, args);
2998
3536
  }
2999
3537
  /**
@@ -3003,6 +3541,7 @@ var Script = class {
3003
3541
  * Following calls will be able to use the cached script
3004
3542
  */
3005
3543
  async exec(keys, args) {
3544
+ await this.init(this.script);
3006
3545
  const res = await this.redis.evalsha(this.sha1, keys, args).catch(async (error) => {
3007
3546
  if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3008
3547
  return await this.redis.eval(this.script, keys, args);
@@ -3014,8 +3553,75 @@ var Script = class {
3014
3553
  /**
3015
3554
  * Compute the sha1 hash of the script and return its hex representation.
3016
3555
  */
3017
- digest(s) {
3018
- 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("");
3019
3625
  }
3020
3626
  };
3021
3627
 
@@ -3093,6 +3699,10 @@ var Redis = class {
3093
3699
  * @see https://redis.io/commands/json.get
3094
3700
  */
3095
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),
3096
3706
  /**
3097
3707
  * @see https://redis.io/commands/json.mget
3098
3708
  */
@@ -3162,8 +3772,36 @@ var Redis = class {
3162
3772
  } catch {
3163
3773
  }
3164
3774
  };
3165
- createScript(script) {
3166
- 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);
3167
3805
  }
3168
3806
  /**
3169
3807
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3250,10 +3888,18 @@ var Redis = class {
3250
3888
  * @see https://redis.io/commands/echo
3251
3889
  */
3252
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);
3253
3895
  /**
3254
3896
  * @see https://redis.io/commands/eval
3255
3897
  */
3256
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);
3257
3903
  /**
3258
3904
  * @see https://redis.io/commands/evalsha
3259
3905
  */
@@ -3318,6 +3964,10 @@ var Redis = class {
3318
3964
  * @see https://redis.io/commands/getdel
3319
3965
  */
3320
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);
3321
3971
  /**
3322
3972
  * @see https://redis.io/commands/getrange
3323
3973
  */
@@ -3334,6 +3984,42 @@ var Redis = class {
3334
3984
  * @see https://redis.io/commands/hexists
3335
3985
  */
3336
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);
3337
4023
  /**
3338
4024
  * @see https://redis.io/commands/hget
3339
4025
  */
@@ -3502,6 +4188,13 @@ var Redis = class {
3502
4188
  * @see https://redis.io/commands/psetex
3503
4189
  */
3504
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
+ };
3505
4198
  /**
3506
4199
  * @see https://redis.io/commands/pttl
3507
4200
  */
@@ -3538,10 +4231,9 @@ var Redis = class {
3538
4231
  * @see https://redis.io/commands/sadd
3539
4232
  */
3540
4233
  sadd = (key, member, ...members) => new SAddCommand([key, member, ...members], this.opts).exec(this.client);
3541
- /**
3542
- * @see https://redis.io/commands/scan
3543
- */
3544
- 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
+ }
3545
4237
  /**
3546
4238
  * @see https://redis.io/commands/scard
3547
4239
  */
@@ -3630,6 +4322,13 @@ var Redis = class {
3630
4322
  * @see https://redis.io/commands/strlen
3631
4323
  */
3632
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
+ };
3633
4332
  /**
3634
4333
  * @see https://redis.io/commands/sunion
3635
4334
  */