@upstash/redis 0.0.0-ci.e9b0c868305a820aaed7d68c8407b277da5a093e-20241008121230 → 0.0.0-ci.ea9f0853d6a86e6b7809a21bff609de1b6e9291d-20250414063537

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;
@@ -46,7 +83,7 @@ var HttpClient = class {
46
83
  };
47
84
  this.upstashSyncToken = "";
48
85
  this.readYourWrites = config.readYourWrites ?? true;
49
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
86
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
50
87
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
51
88
  if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
52
89
  throw new UrlError(this.baseUrl);
@@ -73,22 +110,25 @@ 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
  */
88
128
  backend: this.options.backend
89
129
  };
90
130
  if (!this.hasCredentials) {
91
- throw new Error(
131
+ console.warn(
92
132
  "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
93
133
  );
94
134
  }
@@ -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) {
@@ -115,20 +155,54 @@ var HttpClient = class {
115
155
  break;
116
156
  }
117
157
  error = error_;
118
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
158
+ if (i < this.retry.attempts) {
159
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
160
+ }
119
161
  }
120
162
  }
121
163
  if (!res) {
122
164
  throw error ?? new Error("Exhausted all retries");
123
165
  }
124
- const body = await res.json();
125
166
  if (!res.ok) {
126
- 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)}`);
127
169
  }
128
170
  if (this.readYourWrites) {
129
171
  const headers = res.headers;
130
172
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
131
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();
132
206
  if (this.readYourWrites) {
133
207
  const headers = res.headers;
134
208
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -199,31 +273,6 @@ function merge(obj, key, value) {
199
273
  return obj;
200
274
  }
201
275
 
202
- // pkg/util.ts
203
- function parseRecursive(obj) {
204
- const parsed = Array.isArray(obj) ? obj.map((o) => {
205
- try {
206
- return parseRecursive(o);
207
- } catch {
208
- return o;
209
- }
210
- }) : JSON.parse(obj);
211
- if (typeof parsed === "number" && parsed.toString() !== obj) {
212
- return obj;
213
- }
214
- return parsed;
215
- }
216
- function parseResponse(result) {
217
- try {
218
- return parseRecursive(result);
219
- } catch {
220
- return result;
221
- }
222
- }
223
- function deserializeScanResponse(result) {
224
- return [result[0], ...parseResponse(result.slice(1))];
225
- }
226
-
227
276
  // pkg/commands/command.ts
228
277
  var defaultSerializer = (c) => {
229
278
  switch (typeof c) {
@@ -241,6 +290,11 @@ var Command = class {
241
290
  command;
242
291
  serialize;
243
292
  deserialize;
293
+ headers;
294
+ path;
295
+ onMessage;
296
+ isStreaming;
297
+ signal;
244
298
  /**
245
299
  * Create a new command instance.
246
300
  *
@@ -250,6 +304,11 @@ var Command = class {
250
304
  this.serialize = defaultSerializer;
251
305
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
252
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;
253
312
  if (opts?.latencyLogging) {
254
313
  const originalExec = this.exec.bind(this);
255
314
  this.exec = async (client) => {
@@ -270,7 +329,12 @@ var Command = class {
270
329
  async exec(client) {
271
330
  const { result, error } = await client.request({
272
331
  body: this.command,
273
- 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
274
338
  });
275
339
  if (error) {
276
340
  throw new UpstashError(error);
@@ -288,9 +352,9 @@ function deserialize(result) {
288
352
  return null;
289
353
  }
290
354
  const obj = {};
291
- while (result.length >= 2) {
292
- const key = result.shift();
293
- 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];
294
358
  try {
295
359
  obj[key] = JSON.parse(value);
296
360
  } catch {
@@ -432,6 +496,13 @@ var EchoCommand = class extends Command {
432
496
  }
433
497
  };
434
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
+
435
506
  // pkg/commands/eval.ts
436
507
  var EvalCommand = class extends Command {
437
508
  constructor([script, keys, args], opts) {
@@ -439,6 +510,13 @@ var EvalCommand = class extends Command {
439
510
  }
440
511
  };
441
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
+
442
520
  // pkg/commands/evalsha.ts
443
521
  var EvalshaCommand = class extends Command {
444
522
  constructor([sha, keys, args], opts) {
@@ -446,6 +524,14 @@ var EvalshaCommand = class extends Command {
446
524
  }
447
525
  };
448
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
+
449
535
  // pkg/commands/exists.ts
450
536
  var ExistsCommand = class extends Command {
451
537
  constructor(cmd, opts) {
@@ -662,6 +748,27 @@ var GetDelCommand = class extends Command {
662
748
  }
663
749
  };
664
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
+
665
772
  // pkg/commands/getrange.ts
666
773
  var GetRangeCommand = class extends Command {
667
774
  constructor(cmd, opts) {
@@ -690,6 +797,122 @@ var HExistsCommand = class extends Command {
690
797
  }
691
798
  };
692
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
+
693
916
  // pkg/commands/hget.ts
694
917
  var HGetCommand = class extends Command {
695
918
  constructor(cmd, opts) {
@@ -703,9 +926,9 @@ function deserialize2(result) {
703
926
  return null;
704
927
  }
705
928
  const obj = {};
706
- while (result.length >= 2) {
707
- const key = result.shift();
708
- 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];
709
932
  try {
710
933
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
711
934
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -821,6 +1044,15 @@ var HStrLenCommand = class extends Command {
821
1044
  }
822
1045
  };
823
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
+
824
1056
  // pkg/commands/hvals.ts
825
1057
  var HValsCommand = class extends Command {
826
1058
  constructor(cmd, opts) {
@@ -940,6 +1172,14 @@ var JsonGetCommand = class extends Command {
940
1172
  }
941
1173
  };
942
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
+
943
1183
  // pkg/commands/json_mget.ts
944
1184
  var JsonMGetCommand = class extends Command {
945
1185
  constructor(cmd, opts) {
@@ -1730,15 +1970,15 @@ var XPendingCommand = class extends Command {
1730
1970
  function deserialize4(result) {
1731
1971
  const obj = {};
1732
1972
  for (const e of result) {
1733
- while (e.length >= 2) {
1734
- const streamId = e.shift();
1735
- 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];
1736
1976
  if (!(streamId in obj)) {
1737
1977
  obj[streamId] = {};
1738
1978
  }
1739
- while (entries.length >= 2) {
1740
- const field = entries.shift();
1741
- 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];
1742
1982
  try {
1743
1983
  obj[streamId][field] = JSON.parse(value);
1744
1984
  } catch {
@@ -1827,15 +2067,15 @@ var XRevRangeCommand = class extends Command {
1827
2067
  function deserialize5(result) {
1828
2068
  const obj = {};
1829
2069
  for (const e of result) {
1830
- while (e.length >= 2) {
1831
- const streamId = e.shift();
1832
- 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];
1833
2073
  if (!(streamId in obj)) {
1834
2074
  obj[streamId] = {};
1835
2075
  }
1836
- while (entries.length >= 2) {
1837
- const field = entries.shift();
1838
- 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];
1839
2079
  try {
1840
2080
  obj[streamId][field] = JSON.parse(value);
1841
2081
  } catch {
@@ -2122,9 +2362,9 @@ var Pipeline = class {
2122
2362
  this.multiExec = opts.multiExec ?? false;
2123
2363
  if (this.commandOptions?.latencyLogging) {
2124
2364
  const originalExec = this.exec.bind(this);
2125
- this.exec = async () => {
2365
+ this.exec = async (options) => {
2126
2366
  const start = performance.now();
2127
- const result = await originalExec();
2367
+ const result = await (options ? originalExec(options) : originalExec());
2128
2368
  const end = performance.now();
2129
2369
  const loggerResult = (end - start).toFixed(2);
2130
2370
  console.log(
@@ -2134,19 +2374,7 @@ var Pipeline = class {
2134
2374
  };
2135
2375
  }
2136
2376
  }
2137
- /**
2138
- * Send the pipeline request to upstash.
2139
- *
2140
- * Returns an array with the results of all pipelined commands.
2141
- *
2142
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2143
- * ```ts
2144
- * const p = redis.pipeline()
2145
- * p.get("key")
2146
- * const result = p.exec<[{ greeting: string }]>()
2147
- * ```
2148
- */
2149
- exec = async () => {
2377
+ exec = async (options) => {
2150
2378
  if (this.commands.length === 0) {
2151
2379
  throw new Error("Pipeline is empty");
2152
2380
  }
@@ -2155,7 +2383,12 @@ var Pipeline = class {
2155
2383
  path,
2156
2384
  body: Object.values(this.commands).map((c) => c.command)
2157
2385
  });
2158
- return res.map(({ error, result }, i) => {
2386
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2387
+ return {
2388
+ error,
2389
+ result: this.commands[i].deserialize(result)
2390
+ };
2391
+ }) : res.map(({ error, result }, i) => {
2159
2392
  if (error) {
2160
2393
  throw new UpstashError(
2161
2394
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2241,10 +2474,18 @@ var Pipeline = class {
2241
2474
  * @see https://redis.io/commands/echo
2242
2475
  */
2243
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));
2244
2481
  /**
2245
2482
  * @see https://redis.io/commands/eval
2246
2483
  */
2247
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));
2248
2489
  /**
2249
2490
  * @see https://redis.io/commands/evalsha
2250
2491
  */
@@ -2305,6 +2546,10 @@ var Pipeline = class {
2305
2546
  * @see https://redis.io/commands/getdel
2306
2547
  */
2307
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));
2308
2553
  /**
2309
2554
  * @see https://redis.io/commands/getrange
2310
2555
  */
@@ -2321,6 +2566,42 @@ var Pipeline = class {
2321
2566
  * @see https://redis.io/commands/hexists
2322
2567
  */
2323
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));
2324
2605
  /**
2325
2606
  * @see https://redis.io/commands/hget
2326
2607
  */
@@ -2838,6 +3119,10 @@ var Pipeline = class {
2838
3119
  * @see https://redis.io/commands/json.get
2839
3120
  */
2840
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)),
2841
3126
  /**
2842
3127
  * @see https://redis.io/commands/json.mget
2843
3128
  */
@@ -2949,7 +3234,7 @@ var AutoPipelineExecutor = class {
2949
3234
  executeWithPipeline(pipeline);
2950
3235
  const pipelineDone = this.deferExecution().then(() => {
2951
3236
  if (!this.pipelinePromises.has(pipeline)) {
2952
- const pipelinePromise = pipeline.exec();
3237
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
2953
3238
  this.pipelineCounter += 1;
2954
3239
  this.pipelinePromises.set(pipeline, pipelinePromise);
2955
3240
  this.activePipeline = null;
@@ -2957,7 +3242,11 @@ var AutoPipelineExecutor = class {
2957
3242
  return this.pipelinePromises.get(pipeline);
2958
3243
  });
2959
3244
  const results = await pipelineDone;
2960
- return results[index];
3245
+ const commandResult = results[index];
3246
+ if (commandResult.error) {
3247
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
3248
+ }
3249
+ return commandResult.result;
2961
3250
  }
2962
3251
  async deferExecution() {
2963
3252
  await Promise.resolve();
@@ -2965,6 +3254,183 @@ var AutoPipelineExecutor = class {
2965
3254
  }
2966
3255
  };
2967
3256
 
3257
+ // pkg/commands/psubscribe.ts
3258
+ var PSubscribeCommand = class extends Command {
3259
+ constructor(cmd, opts) {
3260
+ const sseHeaders = {
3261
+ Accept: "text/event-stream",
3262
+ "Cache-Control": "no-cache",
3263
+ Connection: "keep-alive"
3264
+ };
3265
+ super([], {
3266
+ ...opts,
3267
+ headers: sseHeaders,
3268
+ path: ["psubscribe", ...cmd],
3269
+ streamOptions: {
3270
+ isStreaming: true,
3271
+ onMessage: opts?.streamOptions?.onMessage,
3272
+ signal: opts?.streamOptions?.signal
3273
+ }
3274
+ });
3275
+ }
3276
+ };
3277
+
3278
+ // pkg/commands/subscribe.ts
3279
+ var Subscriber = class extends EventTarget {
3280
+ subscriptions;
3281
+ client;
3282
+ listeners;
3283
+ constructor(client, channels, isPattern = false) {
3284
+ super();
3285
+ this.client = client;
3286
+ this.subscriptions = /* @__PURE__ */ new Map();
3287
+ this.listeners = /* @__PURE__ */ new Map();
3288
+ for (const channel of channels) {
3289
+ if (isPattern) {
3290
+ this.subscribeToPattern(channel);
3291
+ } else {
3292
+ this.subscribeToChannel(channel);
3293
+ }
3294
+ }
3295
+ }
3296
+ subscribeToChannel(channel) {
3297
+ const controller = new AbortController();
3298
+ const command = new SubscribeCommand([channel], {
3299
+ streamOptions: {
3300
+ signal: controller.signal,
3301
+ onMessage: (data) => this.handleMessage(data, false)
3302
+ }
3303
+ });
3304
+ command.exec(this.client).catch((error) => {
3305
+ if (error.name !== "AbortError") {
3306
+ this.dispatchToListeners("error", error);
3307
+ }
3308
+ });
3309
+ this.subscriptions.set(channel, {
3310
+ command,
3311
+ controller,
3312
+ isPattern: false
3313
+ });
3314
+ }
3315
+ subscribeToPattern(pattern) {
3316
+ const controller = new AbortController();
3317
+ const command = new PSubscribeCommand([pattern], {
3318
+ streamOptions: {
3319
+ signal: controller.signal,
3320
+ onMessage: (data) => this.handleMessage(data, true)
3321
+ }
3322
+ });
3323
+ command.exec(this.client).catch((error) => {
3324
+ if (error.name !== "AbortError") {
3325
+ this.dispatchToListeners("error", error);
3326
+ }
3327
+ });
3328
+ this.subscriptions.set(pattern, {
3329
+ command,
3330
+ controller,
3331
+ isPattern: true
3332
+ });
3333
+ }
3334
+ handleMessage(data, isPattern) {
3335
+ const messageData = data.replace(/^data:\s*/, "");
3336
+ const firstCommaIndex = messageData.indexOf(",");
3337
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
3338
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
3339
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
3340
+ const type = messageData.slice(0, firstCommaIndex);
3341
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
3342
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3343
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
3344
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
3345
+ try {
3346
+ const message = JSON.parse(messageStr);
3347
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
3348
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
3349
+ } catch (error) {
3350
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3351
+ }
3352
+ } else {
3353
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
3354
+ const messageStr = messageData.slice(secondCommaIndex + 1);
3355
+ try {
3356
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
3357
+ const count = Number.parseInt(messageStr);
3358
+ this.dispatchToListeners(type, count);
3359
+ } else {
3360
+ const message = JSON.parse(messageStr);
3361
+ this.dispatchToListeners(type, { channel, message });
3362
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
3363
+ }
3364
+ } catch (error) {
3365
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
3366
+ }
3367
+ }
3368
+ }
3369
+ }
3370
+ dispatchToListeners(type, data) {
3371
+ const listeners = this.listeners.get(type);
3372
+ if (listeners) {
3373
+ for (const listener of listeners) {
3374
+ listener(data);
3375
+ }
3376
+ }
3377
+ }
3378
+ on(type, listener) {
3379
+ if (!this.listeners.has(type)) {
3380
+ this.listeners.set(type, /* @__PURE__ */ new Set());
3381
+ }
3382
+ this.listeners.get(type)?.add(listener);
3383
+ }
3384
+ removeAllListeners() {
3385
+ this.listeners.clear();
3386
+ }
3387
+ async unsubscribe(channels) {
3388
+ if (channels) {
3389
+ for (const channel of channels) {
3390
+ const subscription = this.subscriptions.get(channel);
3391
+ if (subscription) {
3392
+ try {
3393
+ subscription.controller.abort();
3394
+ } catch {
3395
+ }
3396
+ this.subscriptions.delete(channel);
3397
+ }
3398
+ }
3399
+ } else {
3400
+ for (const subscription of this.subscriptions.values()) {
3401
+ try {
3402
+ subscription.controller.abort();
3403
+ } catch {
3404
+ }
3405
+ }
3406
+ this.subscriptions.clear();
3407
+ this.removeAllListeners();
3408
+ }
3409
+ }
3410
+ getSubscribedChannels() {
3411
+ return [...this.subscriptions.keys()];
3412
+ }
3413
+ };
3414
+ var SubscribeCommand = class extends Command {
3415
+ constructor(cmd, opts) {
3416
+ const sseHeaders = {
3417
+ Accept: "text/event-stream",
3418
+ "Cache-Control": "no-cache",
3419
+ Connection: "keep-alive"
3420
+ };
3421
+ super([], {
3422
+ ...opts,
3423
+ headers: sseHeaders,
3424
+ path: ["subscribe", ...cmd],
3425
+ streamOptions: {
3426
+ isStreaming: true,
3427
+ onMessage: opts?.streamOptions?.onMessage,
3428
+ signal: opts?.streamOptions?.signal
3429
+ }
3430
+ });
3431
+ }
3432
+ };
3433
+
2968
3434
  // pkg/script.ts
2969
3435
  import Hex from "crypto-js/enc-hex.js";
2970
3436
  import sha1 from "crypto-js/sha1.js";
@@ -3012,6 +3478,53 @@ var Script = class {
3012
3478
  }
3013
3479
  };
3014
3480
 
3481
+ // pkg/scriptRo.ts
3482
+ import Hex2 from "crypto-js/enc-hex.js";
3483
+ import sha12 from "crypto-js/sha1.js";
3484
+ var ScriptRO = class {
3485
+ script;
3486
+ sha1;
3487
+ redis;
3488
+ constructor(redis, script) {
3489
+ this.redis = redis;
3490
+ this.sha1 = this.digest(script);
3491
+ this.script = script;
3492
+ }
3493
+ /**
3494
+ * Send an `EVAL_RO` command to redis.
3495
+ */
3496
+ async evalRo(keys, args) {
3497
+ return await this.redis.evalRo(this.script, keys, args);
3498
+ }
3499
+ /**
3500
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3501
+ */
3502
+ async evalshaRo(keys, args) {
3503
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3504
+ }
3505
+ /**
3506
+ * Optimistically try to run `EVALSHA_RO` first.
3507
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3508
+ *
3509
+ * Following calls will be able to use the cached script
3510
+ */
3511
+ async exec(keys, args) {
3512
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3513
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3514
+ return await this.redis.evalRo(this.script, keys, args);
3515
+ }
3516
+ throw error;
3517
+ });
3518
+ return res;
3519
+ }
3520
+ /**
3521
+ * Compute the sha1 hash of the script and return its hex representation.
3522
+ */
3523
+ digest(s) {
3524
+ return Hex2.stringify(sha12(s));
3525
+ }
3526
+ };
3527
+
3015
3528
  // pkg/redis.ts
3016
3529
  var Redis = class {
3017
3530
  client;
@@ -3086,6 +3599,10 @@ var Redis = class {
3086
3599
  * @see https://redis.io/commands/json.get
3087
3600
  */
3088
3601
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3602
+ /**
3603
+ * @see https://redis.io/commands/json.merge
3604
+ */
3605
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3089
3606
  /**
3090
3607
  * @see https://redis.io/commands/json.mget
3091
3608
  */
@@ -3155,8 +3672,36 @@ var Redis = class {
3155
3672
  } catch {
3156
3673
  }
3157
3674
  };
3158
- createScript(script) {
3159
- return new Script(this, script);
3675
+ /**
3676
+ * Creates a new script.
3677
+ *
3678
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3679
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3680
+ * the entire script. Afterwards, the script is cached on the server.
3681
+ *
3682
+ * @param script - The script to create
3683
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3684
+ * @returns A new script
3685
+ *
3686
+ * @example
3687
+ * ```ts
3688
+ * const redis = new Redis({...})
3689
+ *
3690
+ * const script = redis.createScript<string>("return ARGV[1];")
3691
+ * const arg1 = await script.eval([], ["Hello World"])
3692
+ * expect(arg1, "Hello World")
3693
+ * ```
3694
+ * @example
3695
+ * ```ts
3696
+ * const redis = new Redis({...})
3697
+ *
3698
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3699
+ * const arg1 = await script.evalRo([], ["Hello World"])
3700
+ * expect(arg1, "Hello World")
3701
+ * ```
3702
+ */
3703
+ createScript(script, opts) {
3704
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3160
3705
  }
3161
3706
  /**
3162
3707
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3243,14 +3788,26 @@ var Redis = class {
3243
3788
  * @see https://redis.io/commands/echo
3244
3789
  */
3245
3790
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3791
+ /**
3792
+ * @see https://redis.io/commands/eval_ro
3793
+ */
3794
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3246
3795
  /**
3247
3796
  * @see https://redis.io/commands/eval
3248
3797
  */
3249
3798
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3799
+ /**
3800
+ * @see https://redis.io/commands/evalsha_ro
3801
+ */
3802
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3250
3803
  /**
3251
3804
  * @see https://redis.io/commands/evalsha
3252
3805
  */
3253
3806
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3807
+ /**
3808
+ * Generic method to execute any Redis command.
3809
+ */
3810
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3254
3811
  /**
3255
3812
  * @see https://redis.io/commands/exists
3256
3813
  */
@@ -3307,6 +3864,10 @@ var Redis = class {
3307
3864
  * @see https://redis.io/commands/getdel
3308
3865
  */
3309
3866
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3867
+ /**
3868
+ * @see https://redis.io/commands/getex
3869
+ */
3870
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3310
3871
  /**
3311
3872
  * @see https://redis.io/commands/getrange
3312
3873
  */
@@ -3323,6 +3884,42 @@ var Redis = class {
3323
3884
  * @see https://redis.io/commands/hexists
3324
3885
  */
3325
3886
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3887
+ /**
3888
+ * @see https://redis.io/commands/hexpire
3889
+ */
3890
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3891
+ /**
3892
+ * @see https://redis.io/commands/hexpireat
3893
+ */
3894
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3895
+ /**
3896
+ * @see https://redis.io/commands/hexpiretime
3897
+ */
3898
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3899
+ /**
3900
+ * @see https://redis.io/commands/httl
3901
+ */
3902
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3903
+ /**
3904
+ * @see https://redis.io/commands/hpexpire
3905
+ */
3906
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3907
+ /**
3908
+ * @see https://redis.io/commands/hpexpireat
3909
+ */
3910
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3911
+ /**
3912
+ * @see https://redis.io/commands/hpexpiretime
3913
+ */
3914
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
3915
+ /**
3916
+ * @see https://redis.io/commands/hpttl
3917
+ */
3918
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
3919
+ /**
3920
+ * @see https://redis.io/commands/hpersist
3921
+ */
3922
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3326
3923
  /**
3327
3924
  * @see https://redis.io/commands/hget
3328
3925
  */
@@ -3491,6 +4088,13 @@ var Redis = class {
3491
4088
  * @see https://redis.io/commands/psetex
3492
4089
  */
3493
4090
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4091
+ /**
4092
+ * @see https://redis.io/commands/psubscribe
4093
+ */
4094
+ psubscribe = (patterns) => {
4095
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4096
+ return new Subscriber(this.client, patternArray, true);
4097
+ };
3494
4098
  /**
3495
4099
  * @see https://redis.io/commands/pttl
3496
4100
  */
@@ -3619,6 +4223,13 @@ var Redis = class {
3619
4223
  * @see https://redis.io/commands/strlen
3620
4224
  */
3621
4225
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4226
+ /**
4227
+ * @see https://redis.io/commands/subscribe
4228
+ */
4229
+ subscribe = (channels) => {
4230
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4231
+ return new Subscriber(this.client, channelArray);
4232
+ };
3622
4233
  /**
3623
4234
  * @see https://redis.io/commands/sunion
3624
4235
  */