@upstash/redis 0.0.0-ci.a1b6b4bb26c26cc4f4d469694ced68414d9c3c80-20241106094912 → 0.0.0-ci.a1eecf417a1944a8b82f402809dd438d52cdf64a-20250506071248

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,6 +25,43 @@ var UrlError = class extends Error {
25
25
  }
26
26
  };
27
27
 
28
+ // pkg/util.ts
29
+ function parseRecursive(obj) {
30
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
31
+ try {
32
+ return parseRecursive(o);
33
+ } catch {
34
+ return o;
35
+ }
36
+ }) : JSON.parse(obj);
37
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
38
+ return obj;
39
+ }
40
+ return parsed;
41
+ }
42
+ function parseResponse(result) {
43
+ try {
44
+ return parseRecursive(result);
45
+ } catch {
46
+ return result;
47
+ }
48
+ }
49
+ function deserializeScanResponse(result) {
50
+ return [result[0], ...parseResponse(result.slice(1))];
51
+ }
52
+ function mergeHeaders(...headers) {
53
+ const merged = {};
54
+ for (const header of headers) {
55
+ if (!header) continue;
56
+ for (const [key, value] of Object.entries(header)) {
57
+ if (value !== void 0 && value !== null) {
58
+ merged[key] = value;
59
+ }
60
+ }
61
+ }
62
+ return merged;
63
+ }
64
+
28
65
  // pkg/http.ts
29
66
  var HttpClient = class {
30
67
  baseUrl;
@@ -73,15 +110,18 @@ var HttpClient = class {
73
110
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
74
111
  }
75
112
  async request(req) {
113
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
114
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
115
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
76
116
  const requestOptions = {
77
117
  //@ts-expect-error this should throw due to bun regression
78
118
  cache: this.options.cache,
79
119
  method: "POST",
80
- headers: this.headers,
120
+ headers: requestHeaders,
81
121
  body: JSON.stringify(req.body),
82
122
  keepalive: this.options.keepAlive,
83
123
  agent: this.options.agent,
84
- signal: this.options.signal,
124
+ signal: req.signal ?? this.options.signal,
85
125
  /**
86
126
  * Fastly specific
87
127
  */
@@ -100,7 +140,7 @@ var HttpClient = class {
100
140
  let error = null;
101
141
  for (let i = 0; i <= this.retry.attempts; i++) {
102
142
  try {
103
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
143
+ res = await fetch(requestUrl, requestOptions);
104
144
  break;
105
145
  } catch (error_) {
106
146
  if (this.options.signal?.aborted) {
@@ -123,14 +163,46 @@ var HttpClient = class {
123
163
  if (!res) {
124
164
  throw error ?? new Error("Exhausted all retries");
125
165
  }
126
- const body = await res.json();
127
166
  if (!res.ok) {
128
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
167
+ const body2 = await res.json();
168
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
129
169
  }
130
170
  if (this.readYourWrites) {
131
171
  const headers = res.headers;
132
172
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
133
173
  }
174
+ if (isEventStream && req && req.onMessage && res.body) {
175
+ const reader = res.body.getReader();
176
+ const decoder = new TextDecoder();
177
+ (async () => {
178
+ try {
179
+ while (true) {
180
+ const { value, done } = await reader.read();
181
+ if (done) break;
182
+ const chunk = decoder.decode(value);
183
+ const lines = chunk.split("\n");
184
+ for (const line of lines) {
185
+ if (line.startsWith("data: ")) {
186
+ const data = line.slice(6);
187
+ req.onMessage?.(data);
188
+ }
189
+ }
190
+ }
191
+ } catch (error2) {
192
+ if (error2 instanceof Error && error2.name === "AbortError") {
193
+ } else {
194
+ console.error("Stream reading error:", error2);
195
+ }
196
+ } finally {
197
+ try {
198
+ await reader.cancel();
199
+ } catch {
200
+ }
201
+ }
202
+ })();
203
+ return { result: 1 };
204
+ }
205
+ const body = await res.json();
134
206
  if (this.readYourWrites) {
135
207
  const headers = res.headers;
136
208
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -201,31 +273,6 @@ function merge(obj, key, value) {
201
273
  return obj;
202
274
  }
203
275
 
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
276
  // pkg/commands/command.ts
230
277
  var defaultSerializer = (c) => {
231
278
  switch (typeof c) {
@@ -243,6 +290,11 @@ var Command = class {
243
290
  command;
244
291
  serialize;
245
292
  deserialize;
293
+ headers;
294
+ path;
295
+ onMessage;
296
+ isStreaming;
297
+ signal;
246
298
  /**
247
299
  * Create a new command instance.
248
300
  *
@@ -252,6 +304,11 @@ var Command = class {
252
304
  this.serialize = defaultSerializer;
253
305
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
254
306
  this.command = command.map((c) => this.serialize(c));
307
+ this.headers = opts?.headers;
308
+ this.path = opts?.path;
309
+ this.onMessage = opts?.streamOptions?.onMessage;
310
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
311
+ this.signal = opts?.streamOptions?.signal;
255
312
  if (opts?.latencyLogging) {
256
313
  const originalExec = this.exec.bind(this);
257
314
  this.exec = async (client) => {
@@ -272,7 +329,12 @@ var Command = class {
272
329
  async exec(client) {
273
330
  const { result, error } = await client.request({
274
331
  body: this.command,
275
- upstashSyncToken: client.upstashSyncToken
332
+ path: this.path,
333
+ upstashSyncToken: client.upstashSyncToken,
334
+ headers: this.headers,
335
+ onMessage: this.onMessage,
336
+ isStreaming: this.isStreaming,
337
+ signal: this.signal
276
338
  });
277
339
  if (error) {
278
340
  throw new UpstashError(error);
@@ -290,9 +352,9 @@ function deserialize(result) {
290
352
  return null;
291
353
  }
292
354
  const obj = {};
293
- while (result.length >= 2) {
294
- const key = result.shift();
295
- const value = result.shift();
355
+ for (let i = 0; i < result.length; i += 2) {
356
+ const key = result[i];
357
+ const value = result[i + 1];
296
358
  try {
297
359
  obj[key] = JSON.parse(value);
298
360
  } catch {
@@ -434,6 +496,13 @@ var EchoCommand = class extends Command {
434
496
  }
435
497
  };
436
498
 
499
+ // pkg/commands/evalRo.ts
500
+ var EvalROCommand = class extends Command {
501
+ constructor([script, keys, args], opts) {
502
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
503
+ }
504
+ };
505
+
437
506
  // pkg/commands/eval.ts
438
507
  var EvalCommand = class extends Command {
439
508
  constructor([script, keys, args], opts) {
@@ -441,6 +510,13 @@ var EvalCommand = class extends Command {
441
510
  }
442
511
  };
443
512
 
513
+ // pkg/commands/evalshaRo.ts
514
+ var EvalshaROCommand = class extends Command {
515
+ constructor([sha, keys, args], opts) {
516
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
517
+ }
518
+ };
519
+
444
520
  // pkg/commands/evalsha.ts
445
521
  var EvalshaCommand = class extends Command {
446
522
  constructor([sha, keys, args], opts) {
@@ -448,6 +524,14 @@ var EvalshaCommand = class extends Command {
448
524
  }
449
525
  };
450
526
 
527
+ // pkg/commands/exec.ts
528
+ var ExecCommand = class extends Command {
529
+ constructor(cmd, opts) {
530
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
531
+ super(normalizedCmd, opts);
532
+ }
533
+ };
534
+
451
535
  // pkg/commands/exists.ts
452
536
  var ExistsCommand = class extends Command {
453
537
  constructor(cmd, opts) {
@@ -664,6 +748,27 @@ var GetDelCommand = class extends Command {
664
748
  }
665
749
  };
666
750
 
751
+ // pkg/commands/getex.ts
752
+ var GetExCommand = class extends Command {
753
+ constructor([key, opts], cmdOpts) {
754
+ const command = ["getex", key];
755
+ if (opts) {
756
+ if ("ex" in opts && typeof opts.ex === "number") {
757
+ command.push("ex", opts.ex);
758
+ } else if ("px" in opts && typeof opts.px === "number") {
759
+ command.push("px", opts.px);
760
+ } else if ("exat" in opts && typeof opts.exat === "number") {
761
+ command.push("exat", opts.exat);
762
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
763
+ command.push("pxat", opts.pxat);
764
+ } else if ("persist" in opts && opts.persist) {
765
+ command.push("persist");
766
+ }
767
+ }
768
+ super(command, cmdOpts);
769
+ }
770
+ };
771
+
667
772
  // pkg/commands/getrange.ts
668
773
  var GetRangeCommand = class extends Command {
669
774
  constructor(cmd, opts) {
@@ -692,6 +797,122 @@ var HExistsCommand = class extends Command {
692
797
  }
693
798
  };
694
799
 
800
+ // pkg/commands/hexpire.ts
801
+ var HExpireCommand = class extends Command {
802
+ constructor(cmd, opts) {
803
+ const [key, fields, seconds, option] = cmd;
804
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
805
+ super(
806
+ [
807
+ "hexpire",
808
+ key,
809
+ seconds,
810
+ ...option ? [option] : [],
811
+ "FIELDS",
812
+ fieldArray.length,
813
+ ...fieldArray
814
+ ],
815
+ opts
816
+ );
817
+ }
818
+ };
819
+
820
+ // pkg/commands/hexpireat.ts
821
+ var HExpireAtCommand = class extends Command {
822
+ constructor(cmd, opts) {
823
+ const [key, fields, timestamp, option] = cmd;
824
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
825
+ super(
826
+ [
827
+ "hexpireat",
828
+ key,
829
+ timestamp,
830
+ ...option ? [option] : [],
831
+ "FIELDS",
832
+ fieldArray.length,
833
+ ...fieldArray
834
+ ],
835
+ opts
836
+ );
837
+ }
838
+ };
839
+
840
+ // pkg/commands/hexpiretime.ts
841
+ var HExpireTimeCommand = class extends Command {
842
+ constructor(cmd, opts) {
843
+ const [key, fields] = cmd;
844
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
845
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
846
+ }
847
+ };
848
+
849
+ // pkg/commands/hpersist.ts
850
+ var HPersistCommand = class extends Command {
851
+ constructor(cmd, opts) {
852
+ const [key, fields] = cmd;
853
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
854
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
855
+ }
856
+ };
857
+
858
+ // pkg/commands/hpexpire.ts
859
+ var HPExpireCommand = class extends Command {
860
+ constructor(cmd, opts) {
861
+ const [key, fields, milliseconds, option] = cmd;
862
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
863
+ super(
864
+ [
865
+ "hpexpire",
866
+ key,
867
+ milliseconds,
868
+ ...option ? [option] : [],
869
+ "FIELDS",
870
+ fieldArray.length,
871
+ ...fieldArray
872
+ ],
873
+ opts
874
+ );
875
+ }
876
+ };
877
+
878
+ // pkg/commands/hpexpireat.ts
879
+ var HPExpireAtCommand = class extends Command {
880
+ constructor(cmd, opts) {
881
+ const [key, fields, timestamp, option] = cmd;
882
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
883
+ super(
884
+ [
885
+ "hpexpireat",
886
+ key,
887
+ timestamp,
888
+ ...option ? [option] : [],
889
+ "FIELDS",
890
+ fieldArray.length,
891
+ ...fieldArray
892
+ ],
893
+ opts
894
+ );
895
+ }
896
+ };
897
+
898
+ // pkg/commands/hpexpiretime.ts
899
+ var HPExpireTimeCommand = class extends Command {
900
+ constructor(cmd, opts) {
901
+ const [key, fields] = cmd;
902
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
903
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
904
+ }
905
+ };
906
+
907
+ // pkg/commands/hpttl.ts
908
+ var HPTtlCommand = class extends Command {
909
+ constructor(cmd, opts) {
910
+ const [key, fields] = cmd;
911
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
912
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
913
+ }
914
+ };
915
+
695
916
  // pkg/commands/hget.ts
696
917
  var HGetCommand = class extends Command {
697
918
  constructor(cmd, opts) {
@@ -705,9 +926,9 @@ function deserialize2(result) {
705
926
  return null;
706
927
  }
707
928
  const obj = {};
708
- while (result.length >= 2) {
709
- const key = result.shift();
710
- const value = result.shift();
929
+ for (let i = 0; i < result.length; i += 2) {
930
+ const key = result[i];
931
+ const value = result[i + 1];
711
932
  try {
712
933
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
713
934
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -823,6 +1044,15 @@ var HStrLenCommand = class extends Command {
823
1044
  }
824
1045
  };
825
1046
 
1047
+ // pkg/commands/httl.ts
1048
+ var HTtlCommand = class extends Command {
1049
+ constructor(cmd, opts) {
1050
+ const [key, fields] = cmd;
1051
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1052
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1053
+ }
1054
+ };
1055
+
826
1056
  // pkg/commands/hvals.ts
827
1057
  var HValsCommand = class extends Command {
828
1058
  constructor(cmd, opts) {
@@ -942,6 +1172,14 @@ var JsonGetCommand = class extends Command {
942
1172
  }
943
1173
  };
944
1174
 
1175
+ // pkg/commands/json_merge.ts
1176
+ var JsonMergeCommand = class extends Command {
1177
+ constructor(cmd, opts) {
1178
+ const command = ["JSON.MERGE", ...cmd];
1179
+ super(command, opts);
1180
+ }
1181
+ };
1182
+
945
1183
  // pkg/commands/json_mget.ts
946
1184
  var JsonMGetCommand = class extends Command {
947
1185
  constructor(cmd, opts) {
@@ -1732,15 +1970,15 @@ var XPendingCommand = class extends Command {
1732
1970
  function deserialize4(result) {
1733
1971
  const obj = {};
1734
1972
  for (const e of result) {
1735
- while (e.length >= 2) {
1736
- const streamId = e.shift();
1737
- const entries = e.shift();
1973
+ for (let i = 0; i < e.length; i += 2) {
1974
+ const streamId = e[i];
1975
+ const entries = e[i + 1];
1738
1976
  if (!(streamId in obj)) {
1739
1977
  obj[streamId] = {};
1740
1978
  }
1741
- while (entries.length >= 2) {
1742
- const field = entries.shift();
1743
- const value = entries.shift();
1979
+ for (let j = 0; j < entries.length; j += 2) {
1980
+ const field = entries[j];
1981
+ const value = entries[j + 1];
1744
1982
  try {
1745
1983
  obj[streamId][field] = JSON.parse(value);
1746
1984
  } catch {
@@ -1829,15 +2067,15 @@ var XRevRangeCommand = class extends Command {
1829
2067
  function deserialize5(result) {
1830
2068
  const obj = {};
1831
2069
  for (const e of result) {
1832
- while (e.length >= 2) {
1833
- const streamId = e.shift();
1834
- const entries = e.shift();
2070
+ for (let i = 0; i < e.length; i += 2) {
2071
+ const streamId = e[i];
2072
+ const entries = e[i + 1];
1835
2073
  if (!(streamId in obj)) {
1836
2074
  obj[streamId] = {};
1837
2075
  }
1838
- while (entries.length >= 2) {
1839
- const field = entries.shift();
1840
- const value = entries.shift();
2076
+ for (let j = 0; j < entries.length; j += 2) {
2077
+ const field = entries[j];
2078
+ const value = entries[j + 1];
1841
2079
  try {
1842
2080
  obj[streamId][field] = JSON.parse(value);
1843
2081
  } catch {
@@ -2236,10 +2474,18 @@ var Pipeline = class {
2236
2474
  * @see https://redis.io/commands/echo
2237
2475
  */
2238
2476
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2477
+ /**
2478
+ * @see https://redis.io/commands/eval_ro
2479
+ */
2480
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2239
2481
  /**
2240
2482
  * @see https://redis.io/commands/eval
2241
2483
  */
2242
2484
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2485
+ /**
2486
+ * @see https://redis.io/commands/evalsha_ro
2487
+ */
2488
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2243
2489
  /**
2244
2490
  * @see https://redis.io/commands/evalsha
2245
2491
  */
@@ -2300,6 +2546,10 @@ var Pipeline = class {
2300
2546
  * @see https://redis.io/commands/getdel
2301
2547
  */
2302
2548
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2549
+ /**
2550
+ * @see https://redis.io/commands/getex
2551
+ */
2552
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2303
2553
  /**
2304
2554
  * @see https://redis.io/commands/getrange
2305
2555
  */
@@ -2316,6 +2566,42 @@ var Pipeline = class {
2316
2566
  * @see https://redis.io/commands/hexists
2317
2567
  */
2318
2568
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2569
+ /**
2570
+ * @see https://redis.io/commands/hexpire
2571
+ */
2572
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2573
+ /**
2574
+ * @see https://redis.io/commands/hexpireat
2575
+ */
2576
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2577
+ /**
2578
+ * @see https://redis.io/commands/hexpiretime
2579
+ */
2580
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2581
+ /**
2582
+ * @see https://redis.io/commands/httl
2583
+ */
2584
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2585
+ /**
2586
+ * @see https://redis.io/commands/hpexpire
2587
+ */
2588
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2589
+ /**
2590
+ * @see https://redis.io/commands/hpexpireat
2591
+ */
2592
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2593
+ /**
2594
+ * @see https://redis.io/commands/hpexpiretime
2595
+ */
2596
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2597
+ /**
2598
+ * @see https://redis.io/commands/hpttl
2599
+ */
2600
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2601
+ /**
2602
+ * @see https://redis.io/commands/hpersist
2603
+ */
2604
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2319
2605
  /**
2320
2606
  * @see https://redis.io/commands/hget
2321
2607
  */
@@ -2833,6 +3119,10 @@ var Pipeline = class {
2833
3119
  * @see https://redis.io/commands/json.get
2834
3120
  */
2835
3121
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3122
+ /**
3123
+ * @see https://redis.io/commands/json.merge
3124
+ */
3125
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2836
3126
  /**
2837
3127
  * @see https://redis.io/commands/json.mget
2838
3128
  */
@@ -2886,6 +3176,23 @@ var Pipeline = class {
2886
3176
  };
2887
3177
 
2888
3178
  // pkg/auto-pipeline.ts
3179
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
3180
+ "scan",
3181
+ "keys",
3182
+ "flushdb",
3183
+ "flushall",
3184
+ "dbsize",
3185
+ "hscan",
3186
+ "hgetall",
3187
+ "hkeys",
3188
+ "lrange",
3189
+ "sscan",
3190
+ "smembers",
3191
+ "xrange",
3192
+ "xrevrange",
3193
+ "zscan",
3194
+ "zrange"
3195
+ ]);
2889
3196
  function createAutoPipelineProxy(_redis, json) {
2890
3197
  const redis = _redis;
2891
3198
  if (!redis.autoPipelineExecutor) {
@@ -2900,7 +3207,8 @@ function createAutoPipelineProxy(_redis, json) {
2900
3207
  return createAutoPipelineProxy(redis2, true);
2901
3208
  }
2902
3209
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
2903
- if (commandInRedisButNotPipeline) {
3210
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
3211
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
2904
3212
  return redis2[command];
2905
3213
  }
2906
3214
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -2964,6 +3272,183 @@ var AutoPipelineExecutor = class {
2964
3272
  }
2965
3273
  };
2966
3274
 
3275
+ // pkg/commands/psubscribe.ts
3276
+ var PSubscribeCommand = class extends Command {
3277
+ constructor(cmd, opts) {
3278
+ const sseHeaders = {
3279
+ Accept: "text/event-stream",
3280
+ "Cache-Control": "no-cache",
3281
+ Connection: "keep-alive"
3282
+ };
3283
+ super([], {
3284
+ ...opts,
3285
+ headers: sseHeaders,
3286
+ path: ["psubscribe", ...cmd],
3287
+ streamOptions: {
3288
+ isStreaming: true,
3289
+ onMessage: opts?.streamOptions?.onMessage,
3290
+ signal: opts?.streamOptions?.signal
3291
+ }
3292
+ });
3293
+ }
3294
+ };
3295
+
3296
+ // pkg/commands/subscribe.ts
3297
+ var Subscriber = class extends EventTarget {
3298
+ subscriptions;
3299
+ client;
3300
+ listeners;
3301
+ constructor(client, channels, isPattern = false) {
3302
+ super();
3303
+ this.client = client;
3304
+ this.subscriptions = /* @__PURE__ */ new Map();
3305
+ this.listeners = /* @__PURE__ */ new Map();
3306
+ for (const channel of channels) {
3307
+ if (isPattern) {
3308
+ this.subscribeToPattern(channel);
3309
+ } else {
3310
+ this.subscribeToChannel(channel);
3311
+ }
3312
+ }
3313
+ }
3314
+ subscribeToChannel(channel) {
3315
+ const controller = new AbortController();
3316
+ const command = new SubscribeCommand([channel], {
3317
+ streamOptions: {
3318
+ signal: controller.signal,
3319
+ onMessage: (data) => this.handleMessage(data, false)
3320
+ }
3321
+ });
3322
+ command.exec(this.client).catch((error) => {
3323
+ if (error.name !== "AbortError") {
3324
+ this.dispatchToListeners("error", error);
3325
+ }
3326
+ });
3327
+ this.subscriptions.set(channel, {
3328
+ command,
3329
+ controller,
3330
+ isPattern: false
3331
+ });
3332
+ }
3333
+ subscribeToPattern(pattern) {
3334
+ const controller = new AbortController();
3335
+ const command = new PSubscribeCommand([pattern], {
3336
+ streamOptions: {
3337
+ signal: controller.signal,
3338
+ onMessage: (data) => this.handleMessage(data, true)
3339
+ }
3340
+ });
3341
+ command.exec(this.client).catch((error) => {
3342
+ if (error.name !== "AbortError") {
3343
+ this.dispatchToListeners("error", error);
3344
+ }
3345
+ });
3346
+ this.subscriptions.set(pattern, {
3347
+ command,
3348
+ controller,
3349
+ isPattern: true
3350
+ });
3351
+ }
3352
+ handleMessage(data, isPattern) {
3353
+ const messageData = data.replace(/^data:\s*/, "");
3354
+ const firstCommaIndex = messageData.indexOf(",");
3355
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
3356
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
3357
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
3358
+ const type = messageData.slice(0, firstCommaIndex);
3359
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
3360
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3361
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
3362
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
3363
+ try {
3364
+ const message = JSON.parse(messageStr);
3365
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
3366
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
3367
+ } catch (error) {
3368
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3369
+ }
3370
+ } else {
3371
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3372
+ const messageStr = messageData.slice(secondCommaIndex + 1);
3373
+ try {
3374
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
3375
+ const count = Number.parseInt(messageStr);
3376
+ this.dispatchToListeners(type, count);
3377
+ } else {
3378
+ const message = JSON.parse(messageStr);
3379
+ this.dispatchToListeners(type, { channel, message });
3380
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
3381
+ }
3382
+ } catch (error) {
3383
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3384
+ }
3385
+ }
3386
+ }
3387
+ }
3388
+ dispatchToListeners(type, data) {
3389
+ const listeners = this.listeners.get(type);
3390
+ if (listeners) {
3391
+ for (const listener of listeners) {
3392
+ listener(data);
3393
+ }
3394
+ }
3395
+ }
3396
+ on(type, listener) {
3397
+ if (!this.listeners.has(type)) {
3398
+ this.listeners.set(type, /* @__PURE__ */ new Set());
3399
+ }
3400
+ this.listeners.get(type)?.add(listener);
3401
+ }
3402
+ removeAllListeners() {
3403
+ this.listeners.clear();
3404
+ }
3405
+ async unsubscribe(channels) {
3406
+ if (channels) {
3407
+ for (const channel of channels) {
3408
+ const subscription = this.subscriptions.get(channel);
3409
+ if (subscription) {
3410
+ try {
3411
+ subscription.controller.abort();
3412
+ } catch {
3413
+ }
3414
+ this.subscriptions.delete(channel);
3415
+ }
3416
+ }
3417
+ } else {
3418
+ for (const subscription of this.subscriptions.values()) {
3419
+ try {
3420
+ subscription.controller.abort();
3421
+ } catch {
3422
+ }
3423
+ }
3424
+ this.subscriptions.clear();
3425
+ this.removeAllListeners();
3426
+ }
3427
+ }
3428
+ getSubscribedChannels() {
3429
+ return [...this.subscriptions.keys()];
3430
+ }
3431
+ };
3432
+ var SubscribeCommand = class extends Command {
3433
+ constructor(cmd, opts) {
3434
+ const sseHeaders = {
3435
+ Accept: "text/event-stream",
3436
+ "Cache-Control": "no-cache",
3437
+ Connection: "keep-alive"
3438
+ };
3439
+ super([], {
3440
+ ...opts,
3441
+ headers: sseHeaders,
3442
+ path: ["subscribe", ...cmd],
3443
+ streamOptions: {
3444
+ isStreaming: true,
3445
+ onMessage: opts?.streamOptions?.onMessage,
3446
+ signal: opts?.streamOptions?.signal
3447
+ }
3448
+ });
3449
+ }
3450
+ };
3451
+
2967
3452
  // pkg/script.ts
2968
3453
  import Hex from "crypto-js/enc-hex.js";
2969
3454
  import sha1 from "crypto-js/sha1.js";
@@ -3011,6 +3496,53 @@ var Script = class {
3011
3496
  }
3012
3497
  };
3013
3498
 
3499
+ // pkg/scriptRo.ts
3500
+ import Hex2 from "crypto-js/enc-hex.js";
3501
+ import sha12 from "crypto-js/sha1.js";
3502
+ var ScriptRO = class {
3503
+ script;
3504
+ sha1;
3505
+ redis;
3506
+ constructor(redis, script) {
3507
+ this.redis = redis;
3508
+ this.sha1 = this.digest(script);
3509
+ this.script = script;
3510
+ }
3511
+ /**
3512
+ * Send an `EVAL_RO` command to redis.
3513
+ */
3514
+ async evalRo(keys, args) {
3515
+ return await this.redis.evalRo(this.script, keys, args);
3516
+ }
3517
+ /**
3518
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3519
+ */
3520
+ async evalshaRo(keys, args) {
3521
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3522
+ }
3523
+ /**
3524
+ * Optimistically try to run `EVALSHA_RO` first.
3525
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3526
+ *
3527
+ * Following calls will be able to use the cached script
3528
+ */
3529
+ async exec(keys, args) {
3530
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3531
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3532
+ return await this.redis.evalRo(this.script, keys, args);
3533
+ }
3534
+ throw error;
3535
+ });
3536
+ return res;
3537
+ }
3538
+ /**
3539
+ * Compute the sha1 hash of the script and return its hex representation.
3540
+ */
3541
+ digest(s) {
3542
+ return Hex2.stringify(sha12(s));
3543
+ }
3544
+ };
3545
+
3014
3546
  // pkg/redis.ts
3015
3547
  var Redis = class {
3016
3548
  client;
@@ -3085,6 +3617,10 @@ var Redis = class {
3085
3617
  * @see https://redis.io/commands/json.get
3086
3618
  */
3087
3619
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3620
+ /**
3621
+ * @see https://redis.io/commands/json.merge
3622
+ */
3623
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3088
3624
  /**
3089
3625
  * @see https://redis.io/commands/json.mget
3090
3626
  */
@@ -3154,8 +3690,36 @@ var Redis = class {
3154
3690
  } catch {
3155
3691
  }
3156
3692
  };
3157
- createScript(script) {
3158
- return new Script(this, script);
3693
+ /**
3694
+ * Creates a new script.
3695
+ *
3696
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3697
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3698
+ * the entire script. Afterwards, the script is cached on the server.
3699
+ *
3700
+ * @param script - The script to create
3701
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3702
+ * @returns A new script
3703
+ *
3704
+ * @example
3705
+ * ```ts
3706
+ * const redis = new Redis({...})
3707
+ *
3708
+ * const script = redis.createScript<string>("return ARGV[1];")
3709
+ * const arg1 = await script.eval([], ["Hello World"])
3710
+ * expect(arg1, "Hello World")
3711
+ * ```
3712
+ * @example
3713
+ * ```ts
3714
+ * const redis = new Redis({...})
3715
+ *
3716
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3717
+ * const arg1 = await script.evalRo([], ["Hello World"])
3718
+ * expect(arg1, "Hello World")
3719
+ * ```
3720
+ */
3721
+ createScript(script, opts) {
3722
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3159
3723
  }
3160
3724
  /**
3161
3725
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3242,14 +3806,26 @@ var Redis = class {
3242
3806
  * @see https://redis.io/commands/echo
3243
3807
  */
3244
3808
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3809
+ /**
3810
+ * @see https://redis.io/commands/eval_ro
3811
+ */
3812
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3245
3813
  /**
3246
3814
  * @see https://redis.io/commands/eval
3247
3815
  */
3248
3816
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3817
+ /**
3818
+ * @see https://redis.io/commands/evalsha_ro
3819
+ */
3820
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3249
3821
  /**
3250
3822
  * @see https://redis.io/commands/evalsha
3251
3823
  */
3252
3824
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3825
+ /**
3826
+ * Generic method to execute any Redis command.
3827
+ */
3828
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3253
3829
  /**
3254
3830
  * @see https://redis.io/commands/exists
3255
3831
  */
@@ -3306,6 +3882,10 @@ var Redis = class {
3306
3882
  * @see https://redis.io/commands/getdel
3307
3883
  */
3308
3884
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3885
+ /**
3886
+ * @see https://redis.io/commands/getex
3887
+ */
3888
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3309
3889
  /**
3310
3890
  * @see https://redis.io/commands/getrange
3311
3891
  */
@@ -3322,6 +3902,42 @@ var Redis = class {
3322
3902
  * @see https://redis.io/commands/hexists
3323
3903
  */
3324
3904
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3905
+ /**
3906
+ * @see https://redis.io/commands/hexpire
3907
+ */
3908
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3909
+ /**
3910
+ * @see https://redis.io/commands/hexpireat
3911
+ */
3912
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3913
+ /**
3914
+ * @see https://redis.io/commands/hexpiretime
3915
+ */
3916
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3917
+ /**
3918
+ * @see https://redis.io/commands/httl
3919
+ */
3920
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3921
+ /**
3922
+ * @see https://redis.io/commands/hpexpire
3923
+ */
3924
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3925
+ /**
3926
+ * @see https://redis.io/commands/hpexpireat
3927
+ */
3928
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3929
+ /**
3930
+ * @see https://redis.io/commands/hpexpiretime
3931
+ */
3932
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
3933
+ /**
3934
+ * @see https://redis.io/commands/hpttl
3935
+ */
3936
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
3937
+ /**
3938
+ * @see https://redis.io/commands/hpersist
3939
+ */
3940
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3325
3941
  /**
3326
3942
  * @see https://redis.io/commands/hget
3327
3943
  */
@@ -3490,6 +4106,13 @@ var Redis = class {
3490
4106
  * @see https://redis.io/commands/psetex
3491
4107
  */
3492
4108
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4109
+ /**
4110
+ * @see https://redis.io/commands/psubscribe
4111
+ */
4112
+ psubscribe = (patterns) => {
4113
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4114
+ return new Subscriber(this.client, patternArray, true);
4115
+ };
3493
4116
  /**
3494
4117
  * @see https://redis.io/commands/pttl
3495
4118
  */
@@ -3618,6 +4241,13 @@ var Redis = class {
3618
4241
  * @see https://redis.io/commands/strlen
3619
4242
  */
3620
4243
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4244
+ /**
4245
+ * @see https://redis.io/commands/subscribe
4246
+ */
4247
+ subscribe = (channels) => {
4248
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4249
+ return new Subscriber(this.client, channelArray);
4250
+ };
3621
4251
  /**
3622
4252
  * @see https://redis.io/commands/sunion
3623
4253
  */