@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.
package/nodejs.js CHANGED
@@ -56,6 +56,43 @@ var UrlError = class extends Error {
56
56
  }
57
57
  };
58
58
 
59
+ // pkg/util.ts
60
+ function parseRecursive(obj) {
61
+ const parsed = Array.isArray(obj) ? obj.map((o) => {
62
+ try {
63
+ return parseRecursive(o);
64
+ } catch {
65
+ return o;
66
+ }
67
+ }) : JSON.parse(obj);
68
+ if (typeof parsed === "number" && parsed.toString() !== obj) {
69
+ return obj;
70
+ }
71
+ return parsed;
72
+ }
73
+ function parseResponse(result) {
74
+ try {
75
+ return parseRecursive(result);
76
+ } catch {
77
+ return result;
78
+ }
79
+ }
80
+ function deserializeScanResponse(result) {
81
+ return [result[0], ...parseResponse(result.slice(1))];
82
+ }
83
+ function mergeHeaders(...headers) {
84
+ const merged = {};
85
+ for (const header of headers) {
86
+ if (!header) continue;
87
+ for (const [key, value] of Object.entries(header)) {
88
+ if (value !== void 0 && value !== null) {
89
+ merged[key] = value;
90
+ }
91
+ }
92
+ }
93
+ return merged;
94
+ }
95
+
59
96
  // pkg/http.ts
60
97
  var HttpClient = class {
61
98
  baseUrl;
@@ -104,15 +141,18 @@ var HttpClient = class {
104
141
  this.headers = merge(this.headers, "Upstash-Telemetry-Sdk", telemetry.sdk);
105
142
  }
106
143
  async request(req) {
144
+ const requestHeaders = mergeHeaders(this.headers, req.headers ?? {});
145
+ const requestUrl = [this.baseUrl, ...req.path ?? []].join("/");
146
+ const isEventStream = requestHeaders.Accept === "text/event-stream";
107
147
  const requestOptions = {
108
148
  //@ts-expect-error this should throw due to bun regression
109
149
  cache: this.options.cache,
110
150
  method: "POST",
111
- headers: this.headers,
151
+ headers: requestHeaders,
112
152
  body: JSON.stringify(req.body),
113
153
  keepalive: this.options.keepAlive,
114
154
  agent: this.options.agent,
115
- signal: this.options.signal,
155
+ signal: req.signal ?? this.options.signal,
116
156
  /**
117
157
  * Fastly specific
118
158
  */
@@ -131,7 +171,7 @@ var HttpClient = class {
131
171
  let error = null;
132
172
  for (let i = 0; i <= this.retry.attempts; i++) {
133
173
  try {
134
- res = await fetch([this.baseUrl, ...req.path ?? []].join("/"), requestOptions);
174
+ res = await fetch(requestUrl, requestOptions);
135
175
  break;
136
176
  } catch (error_) {
137
177
  if (this.options.signal?.aborted) {
@@ -154,14 +194,46 @@ var HttpClient = class {
154
194
  if (!res) {
155
195
  throw error ?? new Error("Exhausted all retries");
156
196
  }
157
- const body = await res.json();
158
197
  if (!res.ok) {
159
- throw new UpstashError(`${body.error}, command was: ${JSON.stringify(req.body)}`);
198
+ const body2 = await res.json();
199
+ throw new UpstashError(`${body2.error}, command was: ${JSON.stringify(req.body)}`);
160
200
  }
161
201
  if (this.readYourWrites) {
162
202
  const headers = res.headers;
163
203
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
164
204
  }
205
+ if (isEventStream && req && req.onMessage && res.body) {
206
+ const reader = res.body.getReader();
207
+ const decoder = new TextDecoder();
208
+ (async () => {
209
+ try {
210
+ while (true) {
211
+ const { value, done } = await reader.read();
212
+ if (done) break;
213
+ const chunk = decoder.decode(value);
214
+ const lines = chunk.split("\n");
215
+ for (const line of lines) {
216
+ if (line.startsWith("data: ")) {
217
+ const data = line.slice(6);
218
+ req.onMessage?.(data);
219
+ }
220
+ }
221
+ }
222
+ } catch (error2) {
223
+ if (error2 instanceof Error && error2.name === "AbortError") {
224
+ } else {
225
+ console.error("Stream reading error:", error2);
226
+ }
227
+ } finally {
228
+ try {
229
+ await reader.cancel();
230
+ } catch {
231
+ }
232
+ }
233
+ })();
234
+ return { result: 1 };
235
+ }
236
+ const body = await res.json();
165
237
  if (this.readYourWrites) {
166
238
  const headers = res.headers;
167
239
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -233,6 +305,23 @@ function merge(obj, key, value) {
233
305
  }
234
306
 
235
307
  // pkg/auto-pipeline.ts
308
+ var EXCLUDE_COMMANDS = /* @__PURE__ */ new Set([
309
+ "scan",
310
+ "keys",
311
+ "flushdb",
312
+ "flushall",
313
+ "dbsize",
314
+ "hscan",
315
+ "hgetall",
316
+ "hkeys",
317
+ "lrange",
318
+ "sscan",
319
+ "smembers",
320
+ "xrange",
321
+ "xrevrange",
322
+ "zscan",
323
+ "zrange"
324
+ ]);
236
325
  function createAutoPipelineProxy(_redis, json) {
237
326
  const redis = _redis;
238
327
  if (!redis.autoPipelineExecutor) {
@@ -247,7 +336,8 @@ function createAutoPipelineProxy(_redis, json) {
247
336
  return createAutoPipelineProxy(redis2, true);
248
337
  }
249
338
  const commandInRedisButNotPipeline = command in redis2 && !(command in redis2.autoPipelineExecutor.pipeline);
250
- if (commandInRedisButNotPipeline) {
339
+ const isCommandExcluded = EXCLUDE_COMMANDS.has(command);
340
+ if (commandInRedisButNotPipeline || isCommandExcluded) {
251
341
  return redis2[command];
252
342
  }
253
343
  const isFunction = json ? typeof redis2.autoPipelineExecutor.pipeline.json[command] === "function" : typeof redis2.autoPipelineExecutor.pipeline[command] === "function";
@@ -311,31 +401,6 @@ var AutoPipelineExecutor = class {
311
401
  }
312
402
  };
313
403
 
314
- // pkg/util.ts
315
- function parseRecursive(obj) {
316
- const parsed = Array.isArray(obj) ? obj.map((o) => {
317
- try {
318
- return parseRecursive(o);
319
- } catch {
320
- return o;
321
- }
322
- }) : JSON.parse(obj);
323
- if (typeof parsed === "number" && parsed.toString() !== obj) {
324
- return obj;
325
- }
326
- return parsed;
327
- }
328
- function parseResponse(result) {
329
- try {
330
- return parseRecursive(result);
331
- } catch {
332
- return result;
333
- }
334
- }
335
- function deserializeScanResponse(result) {
336
- return [result[0], ...parseResponse(result.slice(1))];
337
- }
338
-
339
404
  // pkg/commands/command.ts
340
405
  var defaultSerializer = (c) => {
341
406
  switch (typeof c) {
@@ -353,6 +418,11 @@ var Command = class {
353
418
  command;
354
419
  serialize;
355
420
  deserialize;
421
+ headers;
422
+ path;
423
+ onMessage;
424
+ isStreaming;
425
+ signal;
356
426
  /**
357
427
  * Create a new command instance.
358
428
  *
@@ -362,6 +432,11 @@ var Command = class {
362
432
  this.serialize = defaultSerializer;
363
433
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
364
434
  this.command = command.map((c) => this.serialize(c));
435
+ this.headers = opts?.headers;
436
+ this.path = opts?.path;
437
+ this.onMessage = opts?.streamOptions?.onMessage;
438
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
439
+ this.signal = opts?.streamOptions?.signal;
365
440
  if (opts?.latencyLogging) {
366
441
  const originalExec = this.exec.bind(this);
367
442
  this.exec = async (client) => {
@@ -382,7 +457,12 @@ var Command = class {
382
457
  async exec(client) {
383
458
  const { result, error } = await client.request({
384
459
  body: this.command,
385
- upstashSyncToken: client.upstashSyncToken
460
+ path: this.path,
461
+ upstashSyncToken: client.upstashSyncToken,
462
+ headers: this.headers,
463
+ onMessage: this.onMessage,
464
+ isStreaming: this.isStreaming,
465
+ signal: this.signal
386
466
  });
387
467
  if (error) {
388
468
  throw new UpstashError(error);
@@ -510,6 +590,13 @@ var EchoCommand = class extends Command {
510
590
  }
511
591
  };
512
592
 
593
+ // pkg/commands/evalRo.ts
594
+ var EvalROCommand = class extends Command {
595
+ constructor([script, keys, args], opts) {
596
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
597
+ }
598
+ };
599
+
513
600
  // pkg/commands/eval.ts
514
601
  var EvalCommand = class extends Command {
515
602
  constructor([script, keys, args], opts) {
@@ -517,6 +604,13 @@ var EvalCommand = class extends Command {
517
604
  }
518
605
  };
519
606
 
607
+ // pkg/commands/evalshaRo.ts
608
+ var EvalshaROCommand = class extends Command {
609
+ constructor([sha, keys, args], opts) {
610
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
611
+ }
612
+ };
613
+
520
614
  // pkg/commands/evalsha.ts
521
615
  var EvalshaCommand = class extends Command {
522
616
  constructor([sha, keys, args], opts) {
@@ -524,6 +618,14 @@ var EvalshaCommand = class extends Command {
524
618
  }
525
619
  };
526
620
 
621
+ // pkg/commands/exec.ts
622
+ var ExecCommand = class extends Command {
623
+ constructor(cmd, opts) {
624
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
625
+ super(normalizedCmd, opts);
626
+ }
627
+ };
628
+
527
629
  // pkg/commands/exists.ts
528
630
  var ExistsCommand = class extends Command {
529
631
  constructor(cmd, opts) {
@@ -740,6 +842,27 @@ var GetDelCommand = class extends Command {
740
842
  }
741
843
  };
742
844
 
845
+ // pkg/commands/getex.ts
846
+ var GetExCommand = class extends Command {
847
+ constructor([key, opts], cmdOpts) {
848
+ const command = ["getex", key];
849
+ if (opts) {
850
+ if ("ex" in opts && typeof opts.ex === "number") {
851
+ command.push("ex", opts.ex);
852
+ } else if ("px" in opts && typeof opts.px === "number") {
853
+ command.push("px", opts.px);
854
+ } else if ("exat" in opts && typeof opts.exat === "number") {
855
+ command.push("exat", opts.exat);
856
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
857
+ command.push("pxat", opts.pxat);
858
+ } else if ("persist" in opts && opts.persist) {
859
+ command.push("persist");
860
+ }
861
+ }
862
+ super(command, cmdOpts);
863
+ }
864
+ };
865
+
743
866
  // pkg/commands/getrange.ts
744
867
  var GetRangeCommand = class extends Command {
745
868
  constructor(cmd, opts) {
@@ -768,6 +891,122 @@ var HExistsCommand = class extends Command {
768
891
  }
769
892
  };
770
893
 
894
+ // pkg/commands/hexpire.ts
895
+ var HExpireCommand = class extends Command {
896
+ constructor(cmd, opts) {
897
+ const [key, fields, seconds, option] = cmd;
898
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
899
+ super(
900
+ [
901
+ "hexpire",
902
+ key,
903
+ seconds,
904
+ ...option ? [option] : [],
905
+ "FIELDS",
906
+ fieldArray.length,
907
+ ...fieldArray
908
+ ],
909
+ opts
910
+ );
911
+ }
912
+ };
913
+
914
+ // pkg/commands/hexpireat.ts
915
+ var HExpireAtCommand = class extends Command {
916
+ constructor(cmd, opts) {
917
+ const [key, fields, timestamp, option] = cmd;
918
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
919
+ super(
920
+ [
921
+ "hexpireat",
922
+ key,
923
+ timestamp,
924
+ ...option ? [option] : [],
925
+ "FIELDS",
926
+ fieldArray.length,
927
+ ...fieldArray
928
+ ],
929
+ opts
930
+ );
931
+ }
932
+ };
933
+
934
+ // pkg/commands/hexpiretime.ts
935
+ var HExpireTimeCommand = class extends Command {
936
+ constructor(cmd, opts) {
937
+ const [key, fields] = cmd;
938
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
939
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
940
+ }
941
+ };
942
+
943
+ // pkg/commands/hpersist.ts
944
+ var HPersistCommand = class extends Command {
945
+ constructor(cmd, opts) {
946
+ const [key, fields] = cmd;
947
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
948
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
949
+ }
950
+ };
951
+
952
+ // pkg/commands/hpexpire.ts
953
+ var HPExpireCommand = class extends Command {
954
+ constructor(cmd, opts) {
955
+ const [key, fields, milliseconds, option] = cmd;
956
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
957
+ super(
958
+ [
959
+ "hpexpire",
960
+ key,
961
+ milliseconds,
962
+ ...option ? [option] : [],
963
+ "FIELDS",
964
+ fieldArray.length,
965
+ ...fieldArray
966
+ ],
967
+ opts
968
+ );
969
+ }
970
+ };
971
+
972
+ // pkg/commands/hpexpireat.ts
973
+ var HPExpireAtCommand = class extends Command {
974
+ constructor(cmd, opts) {
975
+ const [key, fields, timestamp, option] = cmd;
976
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
977
+ super(
978
+ [
979
+ "hpexpireat",
980
+ key,
981
+ timestamp,
982
+ ...option ? [option] : [],
983
+ "FIELDS",
984
+ fieldArray.length,
985
+ ...fieldArray
986
+ ],
987
+ opts
988
+ );
989
+ }
990
+ };
991
+
992
+ // pkg/commands/hpexpiretime.ts
993
+ var HPExpireTimeCommand = class extends Command {
994
+ constructor(cmd, opts) {
995
+ const [key, fields] = cmd;
996
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
997
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
998
+ }
999
+ };
1000
+
1001
+ // pkg/commands/hpttl.ts
1002
+ var HPTtlCommand = class extends Command {
1003
+ constructor(cmd, opts) {
1004
+ const [key, fields] = cmd;
1005
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1006
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1007
+ }
1008
+ };
1009
+
771
1010
  // pkg/commands/hget.ts
772
1011
  var HGetCommand = class extends Command {
773
1012
  constructor(cmd, opts) {
@@ -781,9 +1020,9 @@ function deserialize(result) {
781
1020
  return null;
782
1021
  }
783
1022
  const obj = {};
784
- while (result.length >= 2) {
785
- const key = result.shift();
786
- const value = result.shift();
1023
+ for (let i = 0; i < result.length; i += 2) {
1024
+ const key = result[i];
1025
+ const value = result[i + 1];
787
1026
  try {
788
1027
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
789
1028
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -867,9 +1106,9 @@ function deserialize3(result) {
867
1106
  return null;
868
1107
  }
869
1108
  const obj = {};
870
- while (result.length >= 2) {
871
- const key = result.shift();
872
- const value = result.shift();
1109
+ for (let i = 0; i < result.length; i += 2) {
1110
+ const key = result[i];
1111
+ const value = result[i + 1];
873
1112
  try {
874
1113
  obj[key] = JSON.parse(value);
875
1114
  } catch {
@@ -933,6 +1172,15 @@ var HStrLenCommand = class extends Command {
933
1172
  }
934
1173
  };
935
1174
 
1175
+ // pkg/commands/httl.ts
1176
+ var HTtlCommand = class extends Command {
1177
+ constructor(cmd, opts) {
1178
+ const [key, fields] = cmd;
1179
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1180
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1181
+ }
1182
+ };
1183
+
936
1184
  // pkg/commands/hvals.ts
937
1185
  var HValsCommand = class extends Command {
938
1186
  constructor(cmd, opts) {
@@ -1052,6 +1300,14 @@ var JsonGetCommand = class extends Command {
1052
1300
  }
1053
1301
  };
1054
1302
 
1303
+ // pkg/commands/json_merge.ts
1304
+ var JsonMergeCommand = class extends Command {
1305
+ constructor(cmd, opts) {
1306
+ const command = ["JSON.MERGE", ...cmd];
1307
+ super(command, opts);
1308
+ }
1309
+ };
1310
+
1055
1311
  // pkg/commands/json_mget.ts
1056
1312
  var JsonMGetCommand = class extends Command {
1057
1313
  constructor(cmd, opts) {
@@ -1842,15 +2098,15 @@ var XPendingCommand = class extends Command {
1842
2098
  function deserialize4(result) {
1843
2099
  const obj = {};
1844
2100
  for (const e of result) {
1845
- while (e.length >= 2) {
1846
- const streamId = e.shift();
1847
- const entries = e.shift();
2101
+ for (let i = 0; i < e.length; i += 2) {
2102
+ const streamId = e[i];
2103
+ const entries = e[i + 1];
1848
2104
  if (!(streamId in obj)) {
1849
2105
  obj[streamId] = {};
1850
2106
  }
1851
- while (entries.length >= 2) {
1852
- const field = entries.shift();
1853
- const value = entries.shift();
2107
+ for (let j = 0; j < entries.length; j += 2) {
2108
+ const field = entries[j];
2109
+ const value = entries[j + 1];
1854
2110
  try {
1855
2111
  obj[streamId][field] = JSON.parse(value);
1856
2112
  } catch {
@@ -1939,15 +2195,15 @@ var XRevRangeCommand = class extends Command {
1939
2195
  function deserialize5(result) {
1940
2196
  const obj = {};
1941
2197
  for (const e of result) {
1942
- while (e.length >= 2) {
1943
- const streamId = e.shift();
1944
- const entries = e.shift();
2198
+ for (let i = 0; i < e.length; i += 2) {
2199
+ const streamId = e[i];
2200
+ const entries = e[i + 1];
1945
2201
  if (!(streamId in obj)) {
1946
2202
  obj[streamId] = {};
1947
2203
  }
1948
- while (entries.length >= 2) {
1949
- const field = entries.shift();
1950
- const value = entries.shift();
2204
+ for (let j = 0; j < entries.length; j += 2) {
2205
+ const field = entries[j];
2206
+ const value = entries[j + 1];
1951
2207
  try {
1952
2208
  obj[streamId][field] = JSON.parse(value);
1953
2209
  } catch {
@@ -2206,6 +2462,183 @@ var ZUnionStoreCommand = class extends Command {
2206
2462
  }
2207
2463
  };
2208
2464
 
2465
+ // pkg/commands/psubscribe.ts
2466
+ var PSubscribeCommand = class extends Command {
2467
+ constructor(cmd, opts) {
2468
+ const sseHeaders = {
2469
+ Accept: "text/event-stream",
2470
+ "Cache-Control": "no-cache",
2471
+ Connection: "keep-alive"
2472
+ };
2473
+ super([], {
2474
+ ...opts,
2475
+ headers: sseHeaders,
2476
+ path: ["psubscribe", ...cmd],
2477
+ streamOptions: {
2478
+ isStreaming: true,
2479
+ onMessage: opts?.streamOptions?.onMessage,
2480
+ signal: opts?.streamOptions?.signal
2481
+ }
2482
+ });
2483
+ }
2484
+ };
2485
+
2486
+ // pkg/commands/subscribe.ts
2487
+ var Subscriber = class extends EventTarget {
2488
+ subscriptions;
2489
+ client;
2490
+ listeners;
2491
+ constructor(client, channels, isPattern = false) {
2492
+ super();
2493
+ this.client = client;
2494
+ this.subscriptions = /* @__PURE__ */ new Map();
2495
+ this.listeners = /* @__PURE__ */ new Map();
2496
+ for (const channel of channels) {
2497
+ if (isPattern) {
2498
+ this.subscribeToPattern(channel);
2499
+ } else {
2500
+ this.subscribeToChannel(channel);
2501
+ }
2502
+ }
2503
+ }
2504
+ subscribeToChannel(channel) {
2505
+ const controller = new AbortController();
2506
+ const command = new SubscribeCommand([channel], {
2507
+ streamOptions: {
2508
+ signal: controller.signal,
2509
+ onMessage: (data) => this.handleMessage(data, false)
2510
+ }
2511
+ });
2512
+ command.exec(this.client).catch((error) => {
2513
+ if (error.name !== "AbortError") {
2514
+ this.dispatchToListeners("error", error);
2515
+ }
2516
+ });
2517
+ this.subscriptions.set(channel, {
2518
+ command,
2519
+ controller,
2520
+ isPattern: false
2521
+ });
2522
+ }
2523
+ subscribeToPattern(pattern) {
2524
+ const controller = new AbortController();
2525
+ const command = new PSubscribeCommand([pattern], {
2526
+ streamOptions: {
2527
+ signal: controller.signal,
2528
+ onMessage: (data) => this.handleMessage(data, true)
2529
+ }
2530
+ });
2531
+ command.exec(this.client).catch((error) => {
2532
+ if (error.name !== "AbortError") {
2533
+ this.dispatchToListeners("error", error);
2534
+ }
2535
+ });
2536
+ this.subscriptions.set(pattern, {
2537
+ command,
2538
+ controller,
2539
+ isPattern: true
2540
+ });
2541
+ }
2542
+ handleMessage(data, isPattern) {
2543
+ const messageData = data.replace(/^data:\s*/, "");
2544
+ const firstCommaIndex = messageData.indexOf(",");
2545
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
2546
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
2547
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
2548
+ const type = messageData.slice(0, firstCommaIndex);
2549
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
2550
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2551
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
2552
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
2553
+ try {
2554
+ const message = JSON.parse(messageStr);
2555
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
2556
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
2557
+ } catch (error) {
2558
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2559
+ }
2560
+ } else {
2561
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2562
+ const messageStr = messageData.slice(secondCommaIndex + 1);
2563
+ try {
2564
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
2565
+ const count = Number.parseInt(messageStr);
2566
+ this.dispatchToListeners(type, count);
2567
+ } else {
2568
+ const message = JSON.parse(messageStr);
2569
+ this.dispatchToListeners(type, { channel, message });
2570
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
2571
+ }
2572
+ } catch (error) {
2573
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2574
+ }
2575
+ }
2576
+ }
2577
+ }
2578
+ dispatchToListeners(type, data) {
2579
+ const listeners = this.listeners.get(type);
2580
+ if (listeners) {
2581
+ for (const listener of listeners) {
2582
+ listener(data);
2583
+ }
2584
+ }
2585
+ }
2586
+ on(type, listener) {
2587
+ if (!this.listeners.has(type)) {
2588
+ this.listeners.set(type, /* @__PURE__ */ new Set());
2589
+ }
2590
+ this.listeners.get(type)?.add(listener);
2591
+ }
2592
+ removeAllListeners() {
2593
+ this.listeners.clear();
2594
+ }
2595
+ async unsubscribe(channels) {
2596
+ if (channels) {
2597
+ for (const channel of channels) {
2598
+ const subscription = this.subscriptions.get(channel);
2599
+ if (subscription) {
2600
+ try {
2601
+ subscription.controller.abort();
2602
+ } catch {
2603
+ }
2604
+ this.subscriptions.delete(channel);
2605
+ }
2606
+ }
2607
+ } else {
2608
+ for (const subscription of this.subscriptions.values()) {
2609
+ try {
2610
+ subscription.controller.abort();
2611
+ } catch {
2612
+ }
2613
+ }
2614
+ this.subscriptions.clear();
2615
+ this.removeAllListeners();
2616
+ }
2617
+ }
2618
+ getSubscribedChannels() {
2619
+ return [...this.subscriptions.keys()];
2620
+ }
2621
+ };
2622
+ var SubscribeCommand = class extends Command {
2623
+ constructor(cmd, opts) {
2624
+ const sseHeaders = {
2625
+ Accept: "text/event-stream",
2626
+ "Cache-Control": "no-cache",
2627
+ Connection: "keep-alive"
2628
+ };
2629
+ super([], {
2630
+ ...opts,
2631
+ headers: sseHeaders,
2632
+ path: ["subscribe", ...cmd],
2633
+ streamOptions: {
2634
+ isStreaming: true,
2635
+ onMessage: opts?.streamOptions?.onMessage,
2636
+ signal: opts?.streamOptions?.signal
2637
+ }
2638
+ });
2639
+ }
2640
+ };
2641
+
2209
2642
  // pkg/commands/zdiffstore.ts
2210
2643
  var ZDiffStoreCommand = class extends Command {
2211
2644
  constructor(cmd, opts) {
@@ -2346,10 +2779,18 @@ var Pipeline = class {
2346
2779
  * @see https://redis.io/commands/echo
2347
2780
  */
2348
2781
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2782
+ /**
2783
+ * @see https://redis.io/commands/eval_ro
2784
+ */
2785
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2349
2786
  /**
2350
2787
  * @see https://redis.io/commands/eval
2351
2788
  */
2352
2789
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2790
+ /**
2791
+ * @see https://redis.io/commands/evalsha_ro
2792
+ */
2793
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2353
2794
  /**
2354
2795
  * @see https://redis.io/commands/evalsha
2355
2796
  */
@@ -2410,6 +2851,10 @@ var Pipeline = class {
2410
2851
  * @see https://redis.io/commands/getdel
2411
2852
  */
2412
2853
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2854
+ /**
2855
+ * @see https://redis.io/commands/getex
2856
+ */
2857
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2413
2858
  /**
2414
2859
  * @see https://redis.io/commands/getrange
2415
2860
  */
@@ -2426,6 +2871,42 @@ var Pipeline = class {
2426
2871
  * @see https://redis.io/commands/hexists
2427
2872
  */
2428
2873
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2874
+ /**
2875
+ * @see https://redis.io/commands/hexpire
2876
+ */
2877
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2878
+ /**
2879
+ * @see https://redis.io/commands/hexpireat
2880
+ */
2881
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2882
+ /**
2883
+ * @see https://redis.io/commands/hexpiretime
2884
+ */
2885
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2886
+ /**
2887
+ * @see https://redis.io/commands/httl
2888
+ */
2889
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2890
+ /**
2891
+ * @see https://redis.io/commands/hpexpire
2892
+ */
2893
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2894
+ /**
2895
+ * @see https://redis.io/commands/hpexpireat
2896
+ */
2897
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2898
+ /**
2899
+ * @see https://redis.io/commands/hpexpiretime
2900
+ */
2901
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2902
+ /**
2903
+ * @see https://redis.io/commands/hpttl
2904
+ */
2905
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2906
+ /**
2907
+ * @see https://redis.io/commands/hpersist
2908
+ */
2909
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2429
2910
  /**
2430
2911
  * @see https://redis.io/commands/hget
2431
2912
  */
@@ -2943,6 +3424,10 @@ var Pipeline = class {
2943
3424
  * @see https://redis.io/commands/json.get
2944
3425
  */
2945
3426
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3427
+ /**
3428
+ * @see https://redis.io/commands/json.merge
3429
+ */
3430
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2946
3431
  /**
2947
3432
  * @see https://redis.io/commands/json.mget
2948
3433
  */
@@ -3042,6 +3527,53 @@ var Script = class {
3042
3527
  }
3043
3528
  };
3044
3529
 
3530
+ // pkg/scriptRo.ts
3531
+ var import_enc_hex2 = __toESM(require("crypto-js/enc-hex.js"));
3532
+ var import_sha12 = __toESM(require("crypto-js/sha1.js"));
3533
+ var ScriptRO = class {
3534
+ script;
3535
+ sha1;
3536
+ redis;
3537
+ constructor(redis, script) {
3538
+ this.redis = redis;
3539
+ this.sha1 = this.digest(script);
3540
+ this.script = script;
3541
+ }
3542
+ /**
3543
+ * Send an `EVAL_RO` command to redis.
3544
+ */
3545
+ async evalRo(keys, args) {
3546
+ return await this.redis.evalRo(this.script, keys, args);
3547
+ }
3548
+ /**
3549
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3550
+ */
3551
+ async evalshaRo(keys, args) {
3552
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3553
+ }
3554
+ /**
3555
+ * Optimistically try to run `EVALSHA_RO` first.
3556
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3557
+ *
3558
+ * Following calls will be able to use the cached script
3559
+ */
3560
+ async exec(keys, args) {
3561
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3562
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3563
+ return await this.redis.evalRo(this.script, keys, args);
3564
+ }
3565
+ throw error;
3566
+ });
3567
+ return res;
3568
+ }
3569
+ /**
3570
+ * Compute the sha1 hash of the script and return its hex representation.
3571
+ */
3572
+ digest(s) {
3573
+ return import_enc_hex2.default.stringify((0, import_sha12.default)(s));
3574
+ }
3575
+ };
3576
+
3045
3577
  // pkg/redis.ts
3046
3578
  var Redis = class {
3047
3579
  client;
@@ -3116,6 +3648,10 @@ var Redis = class {
3116
3648
  * @see https://redis.io/commands/json.get
3117
3649
  */
3118
3650
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3651
+ /**
3652
+ * @see https://redis.io/commands/json.merge
3653
+ */
3654
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3119
3655
  /**
3120
3656
  * @see https://redis.io/commands/json.mget
3121
3657
  */
@@ -3185,8 +3721,36 @@ var Redis = class {
3185
3721
  } catch {
3186
3722
  }
3187
3723
  };
3188
- createScript(script) {
3189
- return new Script(this, script);
3724
+ /**
3725
+ * Creates a new script.
3726
+ *
3727
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3728
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3729
+ * the entire script. Afterwards, the script is cached on the server.
3730
+ *
3731
+ * @param script - The script to create
3732
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3733
+ * @returns A new script
3734
+ *
3735
+ * @example
3736
+ * ```ts
3737
+ * const redis = new Redis({...})
3738
+ *
3739
+ * const script = redis.createScript<string>("return ARGV[1];")
3740
+ * const arg1 = await script.eval([], ["Hello World"])
3741
+ * expect(arg1, "Hello World")
3742
+ * ```
3743
+ * @example
3744
+ * ```ts
3745
+ * const redis = new Redis({...})
3746
+ *
3747
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3748
+ * const arg1 = await script.evalRo([], ["Hello World"])
3749
+ * expect(arg1, "Hello World")
3750
+ * ```
3751
+ */
3752
+ createScript(script, opts) {
3753
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3190
3754
  }
3191
3755
  /**
3192
3756
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3273,14 +3837,26 @@ var Redis = class {
3273
3837
  * @see https://redis.io/commands/echo
3274
3838
  */
3275
3839
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3840
+ /**
3841
+ * @see https://redis.io/commands/eval_ro
3842
+ */
3843
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3276
3844
  /**
3277
3845
  * @see https://redis.io/commands/eval
3278
3846
  */
3279
3847
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3848
+ /**
3849
+ * @see https://redis.io/commands/evalsha_ro
3850
+ */
3851
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3280
3852
  /**
3281
3853
  * @see https://redis.io/commands/evalsha
3282
3854
  */
3283
3855
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3856
+ /**
3857
+ * Generic method to execute any Redis command.
3858
+ */
3859
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3284
3860
  /**
3285
3861
  * @see https://redis.io/commands/exists
3286
3862
  */
@@ -3337,6 +3913,10 @@ var Redis = class {
3337
3913
  * @see https://redis.io/commands/getdel
3338
3914
  */
3339
3915
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3916
+ /**
3917
+ * @see https://redis.io/commands/getex
3918
+ */
3919
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3340
3920
  /**
3341
3921
  * @see https://redis.io/commands/getrange
3342
3922
  */
@@ -3353,6 +3933,42 @@ var Redis = class {
3353
3933
  * @see https://redis.io/commands/hexists
3354
3934
  */
3355
3935
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3936
+ /**
3937
+ * @see https://redis.io/commands/hexpire
3938
+ */
3939
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3940
+ /**
3941
+ * @see https://redis.io/commands/hexpireat
3942
+ */
3943
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3944
+ /**
3945
+ * @see https://redis.io/commands/hexpiretime
3946
+ */
3947
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3948
+ /**
3949
+ * @see https://redis.io/commands/httl
3950
+ */
3951
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3952
+ /**
3953
+ * @see https://redis.io/commands/hpexpire
3954
+ */
3955
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3956
+ /**
3957
+ * @see https://redis.io/commands/hpexpireat
3958
+ */
3959
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3960
+ /**
3961
+ * @see https://redis.io/commands/hpexpiretime
3962
+ */
3963
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
3964
+ /**
3965
+ * @see https://redis.io/commands/hpttl
3966
+ */
3967
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
3968
+ /**
3969
+ * @see https://redis.io/commands/hpersist
3970
+ */
3971
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3356
3972
  /**
3357
3973
  * @see https://redis.io/commands/hget
3358
3974
  */
@@ -3521,6 +4137,13 @@ var Redis = class {
3521
4137
  * @see https://redis.io/commands/psetex
3522
4138
  */
3523
4139
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4140
+ /**
4141
+ * @see https://redis.io/commands/psubscribe
4142
+ */
4143
+ psubscribe = (patterns) => {
4144
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4145
+ return new Subscriber(this.client, patternArray, true);
4146
+ };
3524
4147
  /**
3525
4148
  * @see https://redis.io/commands/pttl
3526
4149
  */
@@ -3649,6 +4272,13 @@ var Redis = class {
3649
4272
  * @see https://redis.io/commands/strlen
3650
4273
  */
3651
4274
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4275
+ /**
4276
+ * @see https://redis.io/commands/subscribe
4277
+ */
4278
+ subscribe = (channels) => {
4279
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4280
+ return new Subscriber(this.client, channelArray);
4281
+ };
3652
4282
  /**
3653
4283
  * @see https://redis.io/commands/sunion
3654
4284
  */