@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.
package/fastly.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;
@@ -77,7 +114,7 @@ var HttpClient = class {
77
114
  };
78
115
  this.upstashSyncToken = "";
79
116
  this.readYourWrites = config.readYourWrites ?? true;
80
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
117
+ this.baseUrl = (config.baseUrl || "").replace(/\/$/, "");
81
118
  const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/;
82
119
  if (this.baseUrl && !urlRegex.test(this.baseUrl)) {
83
120
  throw new UrlError(this.baseUrl);
@@ -104,22 +141,25 @@ 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
  */
119
159
  backend: this.options.backend
120
160
  };
121
161
  if (!this.hasCredentials) {
122
- throw new Error(
162
+ console.warn(
123
163
  "[Upstash Redis] Redis client was initialized without url or token. Failed to execute command."
124
164
  );
125
165
  }
@@ -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) {
@@ -146,20 +186,54 @@ var HttpClient = class {
146
186
  break;
147
187
  }
148
188
  error = error_;
149
- await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
189
+ if (i < this.retry.attempts) {
190
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
191
+ }
150
192
  }
151
193
  }
152
194
  if (!res) {
153
195
  throw error ?? new Error("Exhausted all retries");
154
196
  }
155
- const body = await res.json();
156
197
  if (!res.ok) {
157
- 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)}`);
158
200
  }
159
201
  if (this.readYourWrites) {
160
202
  const headers = res.headers;
161
203
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
162
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();
163
237
  if (this.readYourWrites) {
164
238
  const headers = res.headers;
165
239
  this.upstashSyncToken = headers.get("upstash-sync-token") ?? "";
@@ -289,7 +363,7 @@ var AutoPipelineExecutor = class {
289
363
  executeWithPipeline(pipeline);
290
364
  const pipelineDone = this.deferExecution().then(() => {
291
365
  if (!this.pipelinePromises.has(pipeline)) {
292
- const pipelinePromise = pipeline.exec();
366
+ const pipelinePromise = pipeline.exec({ keepErrors: true });
293
367
  this.pipelineCounter += 1;
294
368
  this.pipelinePromises.set(pipeline, pipelinePromise);
295
369
  this.activePipeline = null;
@@ -297,7 +371,11 @@ var AutoPipelineExecutor = class {
297
371
  return this.pipelinePromises.get(pipeline);
298
372
  });
299
373
  const results = await pipelineDone;
300
- return results[index];
374
+ const commandResult = results[index];
375
+ if (commandResult.error) {
376
+ throw new UpstashError(`Command failed: ${commandResult.error}`);
377
+ }
378
+ return commandResult.result;
301
379
  }
302
380
  async deferExecution() {
303
381
  await Promise.resolve();
@@ -305,31 +383,6 @@ var AutoPipelineExecutor = class {
305
383
  }
306
384
  };
307
385
 
308
- // pkg/util.ts
309
- function parseRecursive(obj) {
310
- const parsed = Array.isArray(obj) ? obj.map((o) => {
311
- try {
312
- return parseRecursive(o);
313
- } catch {
314
- return o;
315
- }
316
- }) : JSON.parse(obj);
317
- if (typeof parsed === "number" && parsed.toString() !== obj) {
318
- return obj;
319
- }
320
- return parsed;
321
- }
322
- function parseResponse(result) {
323
- try {
324
- return parseRecursive(result);
325
- } catch {
326
- return result;
327
- }
328
- }
329
- function deserializeScanResponse(result) {
330
- return [result[0], ...parseResponse(result.slice(1))];
331
- }
332
-
333
386
  // pkg/commands/command.ts
334
387
  var defaultSerializer = (c) => {
335
388
  switch (typeof c) {
@@ -347,6 +400,11 @@ var Command = class {
347
400
  command;
348
401
  serialize;
349
402
  deserialize;
403
+ headers;
404
+ path;
405
+ onMessage;
406
+ isStreaming;
407
+ signal;
350
408
  /**
351
409
  * Create a new command instance.
352
410
  *
@@ -356,6 +414,11 @@ var Command = class {
356
414
  this.serialize = defaultSerializer;
357
415
  this.deserialize = opts?.automaticDeserialization === void 0 || opts.automaticDeserialization ? opts?.deserialize ?? parseResponse : (x) => x;
358
416
  this.command = command.map((c) => this.serialize(c));
417
+ this.headers = opts?.headers;
418
+ this.path = opts?.path;
419
+ this.onMessage = opts?.streamOptions?.onMessage;
420
+ this.isStreaming = opts?.streamOptions?.isStreaming ?? false;
421
+ this.signal = opts?.streamOptions?.signal;
359
422
  if (opts?.latencyLogging) {
360
423
  const originalExec = this.exec.bind(this);
361
424
  this.exec = async (client) => {
@@ -376,7 +439,12 @@ var Command = class {
376
439
  async exec(client) {
377
440
  const { result, error } = await client.request({
378
441
  body: this.command,
379
- upstashSyncToken: client.upstashSyncToken
442
+ path: this.path,
443
+ upstashSyncToken: client.upstashSyncToken,
444
+ headers: this.headers,
445
+ onMessage: this.onMessage,
446
+ isStreaming: this.isStreaming,
447
+ signal: this.signal
380
448
  });
381
449
  if (error) {
382
450
  throw new UpstashError(error);
@@ -504,6 +572,13 @@ var EchoCommand = class extends Command {
504
572
  }
505
573
  };
506
574
 
575
+ // pkg/commands/evalRo.ts
576
+ var EvalROCommand = class extends Command {
577
+ constructor([script, keys, args], opts) {
578
+ super(["eval_ro", script, keys.length, ...keys, ...args ?? []], opts);
579
+ }
580
+ };
581
+
507
582
  // pkg/commands/eval.ts
508
583
  var EvalCommand = class extends Command {
509
584
  constructor([script, keys, args], opts) {
@@ -511,6 +586,13 @@ var EvalCommand = class extends Command {
511
586
  }
512
587
  };
513
588
 
589
+ // pkg/commands/evalshaRo.ts
590
+ var EvalshaROCommand = class extends Command {
591
+ constructor([sha, keys, args], opts) {
592
+ super(["evalsha_ro", sha, keys.length, ...keys, ...args ?? []], opts);
593
+ }
594
+ };
595
+
514
596
  // pkg/commands/evalsha.ts
515
597
  var EvalshaCommand = class extends Command {
516
598
  constructor([sha, keys, args], opts) {
@@ -518,6 +600,14 @@ var EvalshaCommand = class extends Command {
518
600
  }
519
601
  };
520
602
 
603
+ // pkg/commands/exec.ts
604
+ var ExecCommand = class extends Command {
605
+ constructor(cmd, opts) {
606
+ const normalizedCmd = cmd.map((arg) => typeof arg === "string" ? arg : String(arg));
607
+ super(normalizedCmd, opts);
608
+ }
609
+ };
610
+
521
611
  // pkg/commands/exists.ts
522
612
  var ExistsCommand = class extends Command {
523
613
  constructor(cmd, opts) {
@@ -734,6 +824,27 @@ var GetDelCommand = class extends Command {
734
824
  }
735
825
  };
736
826
 
827
+ // pkg/commands/getex.ts
828
+ var GetExCommand = class extends Command {
829
+ constructor([key, opts], cmdOpts) {
830
+ const command = ["getex", key];
831
+ if (opts) {
832
+ if ("ex" in opts && typeof opts.ex === "number") {
833
+ command.push("ex", opts.ex);
834
+ } else if ("px" in opts && typeof opts.px === "number") {
835
+ command.push("px", opts.px);
836
+ } else if ("exat" in opts && typeof opts.exat === "number") {
837
+ command.push("exat", opts.exat);
838
+ } else if ("pxat" in opts && typeof opts.pxat === "number") {
839
+ command.push("pxat", opts.pxat);
840
+ } else if ("persist" in opts && opts.persist) {
841
+ command.push("persist");
842
+ }
843
+ }
844
+ super(command, cmdOpts);
845
+ }
846
+ };
847
+
737
848
  // pkg/commands/getrange.ts
738
849
  var GetRangeCommand = class extends Command {
739
850
  constructor(cmd, opts) {
@@ -762,6 +873,122 @@ var HExistsCommand = class extends Command {
762
873
  }
763
874
  };
764
875
 
876
+ // pkg/commands/hexpire.ts
877
+ var HExpireCommand = class extends Command {
878
+ constructor(cmd, opts) {
879
+ const [key, fields, seconds, option] = cmd;
880
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
881
+ super(
882
+ [
883
+ "hexpire",
884
+ key,
885
+ seconds,
886
+ ...option ? [option] : [],
887
+ "FIELDS",
888
+ fieldArray.length,
889
+ ...fieldArray
890
+ ],
891
+ opts
892
+ );
893
+ }
894
+ };
895
+
896
+ // pkg/commands/hexpireat.ts
897
+ var HExpireAtCommand = class extends Command {
898
+ constructor(cmd, opts) {
899
+ const [key, fields, timestamp, option] = cmd;
900
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
901
+ super(
902
+ [
903
+ "hexpireat",
904
+ key,
905
+ timestamp,
906
+ ...option ? [option] : [],
907
+ "FIELDS",
908
+ fieldArray.length,
909
+ ...fieldArray
910
+ ],
911
+ opts
912
+ );
913
+ }
914
+ };
915
+
916
+ // pkg/commands/hexpiretime.ts
917
+ var HExpireTimeCommand = class extends Command {
918
+ constructor(cmd, opts) {
919
+ const [key, fields] = cmd;
920
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
921
+ super(["hexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
922
+ }
923
+ };
924
+
925
+ // pkg/commands/hpersist.ts
926
+ var HPersistCommand = class extends Command {
927
+ constructor(cmd, opts) {
928
+ const [key, fields] = cmd;
929
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
930
+ super(["hpersist", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
931
+ }
932
+ };
933
+
934
+ // pkg/commands/hpexpire.ts
935
+ var HPExpireCommand = class extends Command {
936
+ constructor(cmd, opts) {
937
+ const [key, fields, milliseconds, option] = cmd;
938
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
939
+ super(
940
+ [
941
+ "hpexpire",
942
+ key,
943
+ milliseconds,
944
+ ...option ? [option] : [],
945
+ "FIELDS",
946
+ fieldArray.length,
947
+ ...fieldArray
948
+ ],
949
+ opts
950
+ );
951
+ }
952
+ };
953
+
954
+ // pkg/commands/hpexpireat.ts
955
+ var HPExpireAtCommand = class extends Command {
956
+ constructor(cmd, opts) {
957
+ const [key, fields, timestamp, option] = cmd;
958
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
959
+ super(
960
+ [
961
+ "hpexpireat",
962
+ key,
963
+ timestamp,
964
+ ...option ? [option] : [],
965
+ "FIELDS",
966
+ fieldArray.length,
967
+ ...fieldArray
968
+ ],
969
+ opts
970
+ );
971
+ }
972
+ };
973
+
974
+ // pkg/commands/hpexpiretime.ts
975
+ var HPExpireTimeCommand = class extends Command {
976
+ constructor(cmd, opts) {
977
+ const [key, fields] = cmd;
978
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
979
+ super(["hpexpiretime", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
980
+ }
981
+ };
982
+
983
+ // pkg/commands/hpttl.ts
984
+ var HPTtlCommand = class extends Command {
985
+ constructor(cmd, opts) {
986
+ const [key, fields] = cmd;
987
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
988
+ super(["hpttl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
989
+ }
990
+ };
991
+
765
992
  // pkg/commands/hget.ts
766
993
  var HGetCommand = class extends Command {
767
994
  constructor(cmd, opts) {
@@ -775,9 +1002,9 @@ function deserialize(result) {
775
1002
  return null;
776
1003
  }
777
1004
  const obj = {};
778
- while (result.length >= 2) {
779
- const key = result.shift();
780
- const value = result.shift();
1005
+ for (let i = 0; i < result.length; i += 2) {
1006
+ const key = result[i];
1007
+ const value = result[i + 1];
781
1008
  try {
782
1009
  const valueIsNumberAndNotSafeInteger = !Number.isNaN(Number(value)) && !Number.isSafeInteger(Number(value));
783
1010
  obj[key] = valueIsNumberAndNotSafeInteger ? value : JSON.parse(value);
@@ -861,9 +1088,9 @@ function deserialize3(result) {
861
1088
  return null;
862
1089
  }
863
1090
  const obj = {};
864
- while (result.length >= 2) {
865
- const key = result.shift();
866
- const value = result.shift();
1091
+ for (let i = 0; i < result.length; i += 2) {
1092
+ const key = result[i];
1093
+ const value = result[i + 1];
867
1094
  try {
868
1095
  obj[key] = JSON.parse(value);
869
1096
  } catch {
@@ -927,6 +1154,15 @@ var HStrLenCommand = class extends Command {
927
1154
  }
928
1155
  };
929
1156
 
1157
+ // pkg/commands/httl.ts
1158
+ var HTtlCommand = class extends Command {
1159
+ constructor(cmd, opts) {
1160
+ const [key, fields] = cmd;
1161
+ const fieldArray = Array.isArray(fields) ? fields : [fields];
1162
+ super(["httl", key, "FIELDS", fieldArray.length, ...fieldArray], opts);
1163
+ }
1164
+ };
1165
+
930
1166
  // pkg/commands/hvals.ts
931
1167
  var HValsCommand = class extends Command {
932
1168
  constructor(cmd, opts) {
@@ -1046,6 +1282,14 @@ var JsonGetCommand = class extends Command {
1046
1282
  }
1047
1283
  };
1048
1284
 
1285
+ // pkg/commands/json_merge.ts
1286
+ var JsonMergeCommand = class extends Command {
1287
+ constructor(cmd, opts) {
1288
+ const command = ["JSON.MERGE", ...cmd];
1289
+ super(command, opts);
1290
+ }
1291
+ };
1292
+
1049
1293
  // pkg/commands/json_mget.ts
1050
1294
  var JsonMGetCommand = class extends Command {
1051
1295
  constructor(cmd, opts) {
@@ -1836,15 +2080,15 @@ var XPendingCommand = class extends Command {
1836
2080
  function deserialize4(result) {
1837
2081
  const obj = {};
1838
2082
  for (const e of result) {
1839
- while (e.length >= 2) {
1840
- const streamId = e.shift();
1841
- const entries = e.shift();
2083
+ for (let i = 0; i < e.length; i += 2) {
2084
+ const streamId = e[i];
2085
+ const entries = e[i + 1];
1842
2086
  if (!(streamId in obj)) {
1843
2087
  obj[streamId] = {};
1844
2088
  }
1845
- while (entries.length >= 2) {
1846
- const field = entries.shift();
1847
- const value = entries.shift();
2089
+ for (let j = 0; j < entries.length; j += 2) {
2090
+ const field = entries[j];
2091
+ const value = entries[j + 1];
1848
2092
  try {
1849
2093
  obj[streamId][field] = JSON.parse(value);
1850
2094
  } catch {
@@ -1933,15 +2177,15 @@ var XRevRangeCommand = class extends Command {
1933
2177
  function deserialize5(result) {
1934
2178
  const obj = {};
1935
2179
  for (const e of result) {
1936
- while (e.length >= 2) {
1937
- const streamId = e.shift();
1938
- const entries = e.shift();
2180
+ for (let i = 0; i < e.length; i += 2) {
2181
+ const streamId = e[i];
2182
+ const entries = e[i + 1];
1939
2183
  if (!(streamId in obj)) {
1940
2184
  obj[streamId] = {};
1941
2185
  }
1942
- while (entries.length >= 2) {
1943
- const field = entries.shift();
1944
- const value = entries.shift();
2186
+ for (let j = 0; j < entries.length; j += 2) {
2187
+ const field = entries[j];
2188
+ const value = entries[j + 1];
1945
2189
  try {
1946
2190
  obj[streamId][field] = JSON.parse(value);
1947
2191
  } catch {
@@ -2200,6 +2444,183 @@ var ZUnionStoreCommand = class extends Command {
2200
2444
  }
2201
2445
  };
2202
2446
 
2447
+ // pkg/commands/psubscribe.ts
2448
+ var PSubscribeCommand = class extends Command {
2449
+ constructor(cmd, opts) {
2450
+ const sseHeaders = {
2451
+ Accept: "text/event-stream",
2452
+ "Cache-Control": "no-cache",
2453
+ Connection: "keep-alive"
2454
+ };
2455
+ super([], {
2456
+ ...opts,
2457
+ headers: sseHeaders,
2458
+ path: ["psubscribe", ...cmd],
2459
+ streamOptions: {
2460
+ isStreaming: true,
2461
+ onMessage: opts?.streamOptions?.onMessage,
2462
+ signal: opts?.streamOptions?.signal
2463
+ }
2464
+ });
2465
+ }
2466
+ };
2467
+
2468
+ // pkg/commands/subscribe.ts
2469
+ var Subscriber = class extends EventTarget {
2470
+ subscriptions;
2471
+ client;
2472
+ listeners;
2473
+ constructor(client, channels, isPattern = false) {
2474
+ super();
2475
+ this.client = client;
2476
+ this.subscriptions = /* @__PURE__ */ new Map();
2477
+ this.listeners = /* @__PURE__ */ new Map();
2478
+ for (const channel of channels) {
2479
+ if (isPattern) {
2480
+ this.subscribeToPattern(channel);
2481
+ } else {
2482
+ this.subscribeToChannel(channel);
2483
+ }
2484
+ }
2485
+ }
2486
+ subscribeToChannel(channel) {
2487
+ const controller = new AbortController();
2488
+ const command = new SubscribeCommand([channel], {
2489
+ streamOptions: {
2490
+ signal: controller.signal,
2491
+ onMessage: (data) => this.handleMessage(data, false)
2492
+ }
2493
+ });
2494
+ command.exec(this.client).catch((error) => {
2495
+ if (error.name !== "AbortError") {
2496
+ this.dispatchToListeners("error", error);
2497
+ }
2498
+ });
2499
+ this.subscriptions.set(channel, {
2500
+ command,
2501
+ controller,
2502
+ isPattern: false
2503
+ });
2504
+ }
2505
+ subscribeToPattern(pattern) {
2506
+ const controller = new AbortController();
2507
+ const command = new PSubscribeCommand([pattern], {
2508
+ streamOptions: {
2509
+ signal: controller.signal,
2510
+ onMessage: (data) => this.handleMessage(data, true)
2511
+ }
2512
+ });
2513
+ command.exec(this.client).catch((error) => {
2514
+ if (error.name !== "AbortError") {
2515
+ this.dispatchToListeners("error", error);
2516
+ }
2517
+ });
2518
+ this.subscriptions.set(pattern, {
2519
+ command,
2520
+ controller,
2521
+ isPattern: true
2522
+ });
2523
+ }
2524
+ handleMessage(data, isPattern) {
2525
+ const messageData = data.replace(/^data:\s*/, "");
2526
+ const firstCommaIndex = messageData.indexOf(",");
2527
+ const secondCommaIndex = messageData.indexOf(",", firstCommaIndex + 1);
2528
+ const thirdCommaIndex = isPattern ? messageData.indexOf(",", secondCommaIndex + 1) : -1;
2529
+ if (firstCommaIndex !== -1 && secondCommaIndex !== -1) {
2530
+ const type = messageData.slice(0, firstCommaIndex);
2531
+ if (isPattern && type === "pmessage" && thirdCommaIndex !== -1) {
2532
+ const pattern = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2533
+ const channel = messageData.slice(secondCommaIndex + 1, thirdCommaIndex);
2534
+ const messageStr = messageData.slice(thirdCommaIndex + 1);
2535
+ try {
2536
+ const message = JSON.parse(messageStr);
2537
+ this.dispatchToListeners("pmessage", { pattern, channel, message });
2538
+ this.dispatchToListeners(`pmessage:${pattern}`, { pattern, channel, message });
2539
+ } catch (error) {
2540
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2541
+ }
2542
+ } else {
2543
+ const channel = messageData.slice(firstCommaIndex + 1, secondCommaIndex);
2544
+ const messageStr = messageData.slice(secondCommaIndex + 1);
2545
+ try {
2546
+ if (type === "subscribe" || type === "psubscribe" || type === "unsubscribe" || type === "punsubscribe") {
2547
+ const count = Number.parseInt(messageStr);
2548
+ this.dispatchToListeners(type, count);
2549
+ } else {
2550
+ const message = JSON.parse(messageStr);
2551
+ this.dispatchToListeners(type, { channel, message });
2552
+ this.dispatchToListeners(`${type}:${channel}`, { channel, message });
2553
+ }
2554
+ } catch (error) {
2555
+ this.dispatchToListeners("error", new Error(`Failed to parse message: ${error}`));
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ dispatchToListeners(type, data) {
2561
+ const listeners = this.listeners.get(type);
2562
+ if (listeners) {
2563
+ for (const listener of listeners) {
2564
+ listener(data);
2565
+ }
2566
+ }
2567
+ }
2568
+ on(type, listener) {
2569
+ if (!this.listeners.has(type)) {
2570
+ this.listeners.set(type, /* @__PURE__ */ new Set());
2571
+ }
2572
+ this.listeners.get(type)?.add(listener);
2573
+ }
2574
+ removeAllListeners() {
2575
+ this.listeners.clear();
2576
+ }
2577
+ async unsubscribe(channels) {
2578
+ if (channels) {
2579
+ for (const channel of channels) {
2580
+ const subscription = this.subscriptions.get(channel);
2581
+ if (subscription) {
2582
+ try {
2583
+ subscription.controller.abort();
2584
+ } catch {
2585
+ }
2586
+ this.subscriptions.delete(channel);
2587
+ }
2588
+ }
2589
+ } else {
2590
+ for (const subscription of this.subscriptions.values()) {
2591
+ try {
2592
+ subscription.controller.abort();
2593
+ } catch {
2594
+ }
2595
+ }
2596
+ this.subscriptions.clear();
2597
+ this.removeAllListeners();
2598
+ }
2599
+ }
2600
+ getSubscribedChannels() {
2601
+ return [...this.subscriptions.keys()];
2602
+ }
2603
+ };
2604
+ var SubscribeCommand = class extends Command {
2605
+ constructor(cmd, opts) {
2606
+ const sseHeaders = {
2607
+ Accept: "text/event-stream",
2608
+ "Cache-Control": "no-cache",
2609
+ Connection: "keep-alive"
2610
+ };
2611
+ super([], {
2612
+ ...opts,
2613
+ headers: sseHeaders,
2614
+ path: ["subscribe", ...cmd],
2615
+ streamOptions: {
2616
+ isStreaming: true,
2617
+ onMessage: opts?.streamOptions?.onMessage,
2618
+ signal: opts?.streamOptions?.signal
2619
+ }
2620
+ });
2621
+ }
2622
+ };
2623
+
2203
2624
  // pkg/commands/zdiffstore.ts
2204
2625
  var ZDiffStoreCommand = class extends Command {
2205
2626
  constructor(cmd, opts) {
@@ -2228,9 +2649,9 @@ var Pipeline = class {
2228
2649
  this.multiExec = opts.multiExec ?? false;
2229
2650
  if (this.commandOptions?.latencyLogging) {
2230
2651
  const originalExec = this.exec.bind(this);
2231
- this.exec = async () => {
2652
+ this.exec = async (options) => {
2232
2653
  const start = performance.now();
2233
- const result = await originalExec();
2654
+ const result = await (options ? originalExec(options) : originalExec());
2234
2655
  const end = performance.now();
2235
2656
  const loggerResult = (end - start).toFixed(2);
2236
2657
  console.log(
@@ -2240,19 +2661,7 @@ var Pipeline = class {
2240
2661
  };
2241
2662
  }
2242
2663
  }
2243
- /**
2244
- * Send the pipeline request to upstash.
2245
- *
2246
- * Returns an array with the results of all pipelined commands.
2247
- *
2248
- * If all commands are statically chained from start to finish, types are inferred. You can still define a return type manually if necessary though:
2249
- * ```ts
2250
- * const p = redis.pipeline()
2251
- * p.get("key")
2252
- * const result = p.exec<[{ greeting: string }]>()
2253
- * ```
2254
- */
2255
- exec = async () => {
2664
+ exec = async (options) => {
2256
2665
  if (this.commands.length === 0) {
2257
2666
  throw new Error("Pipeline is empty");
2258
2667
  }
@@ -2261,7 +2670,12 @@ var Pipeline = class {
2261
2670
  path,
2262
2671
  body: Object.values(this.commands).map((c) => c.command)
2263
2672
  });
2264
- return res.map(({ error, result }, i) => {
2673
+ return options?.keepErrors ? res.map(({ error, result }, i) => {
2674
+ return {
2675
+ error,
2676
+ result: this.commands[i].deserialize(result)
2677
+ };
2678
+ }) : res.map(({ error, result }, i) => {
2265
2679
  if (error) {
2266
2680
  throw new UpstashError(
2267
2681
  `Command ${i + 1} [ ${this.commands[i].command[0]} ] failed: ${error}`
@@ -2347,10 +2761,18 @@ var Pipeline = class {
2347
2761
  * @see https://redis.io/commands/echo
2348
2762
  */
2349
2763
  echo = (...args) => this.chain(new EchoCommand(args, this.commandOptions));
2764
+ /**
2765
+ * @see https://redis.io/commands/eval_ro
2766
+ */
2767
+ evalRo = (...args) => this.chain(new EvalROCommand(args, this.commandOptions));
2350
2768
  /**
2351
2769
  * @see https://redis.io/commands/eval
2352
2770
  */
2353
2771
  eval = (...args) => this.chain(new EvalCommand(args, this.commandOptions));
2772
+ /**
2773
+ * @see https://redis.io/commands/evalsha_ro
2774
+ */
2775
+ evalshaRo = (...args) => this.chain(new EvalshaROCommand(args, this.commandOptions));
2354
2776
  /**
2355
2777
  * @see https://redis.io/commands/evalsha
2356
2778
  */
@@ -2411,6 +2833,10 @@ var Pipeline = class {
2411
2833
  * @see https://redis.io/commands/getdel
2412
2834
  */
2413
2835
  getdel = (...args) => this.chain(new GetDelCommand(args, this.commandOptions));
2836
+ /**
2837
+ * @see https://redis.io/commands/getex
2838
+ */
2839
+ getex = (...args) => this.chain(new GetExCommand(args, this.commandOptions));
2414
2840
  /**
2415
2841
  * @see https://redis.io/commands/getrange
2416
2842
  */
@@ -2427,6 +2853,42 @@ var Pipeline = class {
2427
2853
  * @see https://redis.io/commands/hexists
2428
2854
  */
2429
2855
  hexists = (...args) => this.chain(new HExistsCommand(args, this.commandOptions));
2856
+ /**
2857
+ * @see https://redis.io/commands/hexpire
2858
+ */
2859
+ hexpire = (...args) => this.chain(new HExpireCommand(args, this.commandOptions));
2860
+ /**
2861
+ * @see https://redis.io/commands/hexpireat
2862
+ */
2863
+ hexpireat = (...args) => this.chain(new HExpireAtCommand(args, this.commandOptions));
2864
+ /**
2865
+ * @see https://redis.io/commands/hexpiretime
2866
+ */
2867
+ hexpiretime = (...args) => this.chain(new HExpireTimeCommand(args, this.commandOptions));
2868
+ /**
2869
+ * @see https://redis.io/commands/httl
2870
+ */
2871
+ httl = (...args) => this.chain(new HTtlCommand(args, this.commandOptions));
2872
+ /**
2873
+ * @see https://redis.io/commands/hpexpire
2874
+ */
2875
+ hpexpire = (...args) => this.chain(new HPExpireCommand(args, this.commandOptions));
2876
+ /**
2877
+ * @see https://redis.io/commands/hpexpireat
2878
+ */
2879
+ hpexpireat = (...args) => this.chain(new HPExpireAtCommand(args, this.commandOptions));
2880
+ /**
2881
+ * @see https://redis.io/commands/hpexpiretime
2882
+ */
2883
+ hpexpiretime = (...args) => this.chain(new HPExpireTimeCommand(args, this.commandOptions));
2884
+ /**
2885
+ * @see https://redis.io/commands/hpttl
2886
+ */
2887
+ hpttl = (...args) => this.chain(new HPTtlCommand(args, this.commandOptions));
2888
+ /**
2889
+ * @see https://redis.io/commands/hpersist
2890
+ */
2891
+ hpersist = (...args) => this.chain(new HPersistCommand(args, this.commandOptions));
2430
2892
  /**
2431
2893
  * @see https://redis.io/commands/hget
2432
2894
  */
@@ -2944,6 +3406,10 @@ var Pipeline = class {
2944
3406
  * @see https://redis.io/commands/json.get
2945
3407
  */
2946
3408
  get: (...args) => this.chain(new JsonGetCommand(args, this.commandOptions)),
3409
+ /**
3410
+ * @see https://redis.io/commands/json.merge
3411
+ */
3412
+ merge: (...args) => this.chain(new JsonMergeCommand(args, this.commandOptions)),
2947
3413
  /**
2948
3414
  * @see https://redis.io/commands/json.mget
2949
3415
  */
@@ -3043,6 +3509,53 @@ var Script = class {
3043
3509
  }
3044
3510
  };
3045
3511
 
3512
+ // pkg/scriptRo.ts
3513
+ var import_enc_hex2 = __toESM(require("crypto-js/enc-hex.js"));
3514
+ var import_sha12 = __toESM(require("crypto-js/sha1.js"));
3515
+ var ScriptRO = class {
3516
+ script;
3517
+ sha1;
3518
+ redis;
3519
+ constructor(redis, script) {
3520
+ this.redis = redis;
3521
+ this.sha1 = this.digest(script);
3522
+ this.script = script;
3523
+ }
3524
+ /**
3525
+ * Send an `EVAL_RO` command to redis.
3526
+ */
3527
+ async evalRo(keys, args) {
3528
+ return await this.redis.evalRo(this.script, keys, args);
3529
+ }
3530
+ /**
3531
+ * Calculates the sha1 hash of the script and then calls `EVALSHA_RO`.
3532
+ */
3533
+ async evalshaRo(keys, args) {
3534
+ return await this.redis.evalshaRo(this.sha1, keys, args);
3535
+ }
3536
+ /**
3537
+ * Optimistically try to run `EVALSHA_RO` first.
3538
+ * If the script is not loaded in redis, it will fall back and try again with `EVAL_RO`.
3539
+ *
3540
+ * Following calls will be able to use the cached script
3541
+ */
3542
+ async exec(keys, args) {
3543
+ const res = await this.redis.evalshaRo(this.sha1, keys, args).catch(async (error) => {
3544
+ if (error instanceof Error && error.message.toLowerCase().includes("noscript")) {
3545
+ return await this.redis.evalRo(this.script, keys, args);
3546
+ }
3547
+ throw error;
3548
+ });
3549
+ return res;
3550
+ }
3551
+ /**
3552
+ * Compute the sha1 hash of the script and return its hex representation.
3553
+ */
3554
+ digest(s) {
3555
+ return import_enc_hex2.default.stringify((0, import_sha12.default)(s));
3556
+ }
3557
+ };
3558
+
3046
3559
  // pkg/redis.ts
3047
3560
  var Redis = class {
3048
3561
  client;
@@ -3117,6 +3630,10 @@ var Redis = class {
3117
3630
  * @see https://redis.io/commands/json.get
3118
3631
  */
3119
3632
  get: (...args) => new JsonGetCommand(args, this.opts).exec(this.client),
3633
+ /**
3634
+ * @see https://redis.io/commands/json.merge
3635
+ */
3636
+ merge: (...args) => new JsonMergeCommand(args, this.opts).exec(this.client),
3120
3637
  /**
3121
3638
  * @see https://redis.io/commands/json.mget
3122
3639
  */
@@ -3186,8 +3703,36 @@ var Redis = class {
3186
3703
  } catch {
3187
3704
  }
3188
3705
  };
3189
- createScript(script) {
3190
- return new Script(this, script);
3706
+ /**
3707
+ * Creates a new script.
3708
+ *
3709
+ * Scripts offer the ability to optimistically try to execute a script without having to send the
3710
+ * entire script to the server. If the script is loaded on the server, it tries again by sending
3711
+ * the entire script. Afterwards, the script is cached on the server.
3712
+ *
3713
+ * @param script - The script to create
3714
+ * @param opts - Optional options to pass to the script `{ readonly?: boolean }`
3715
+ * @returns A new script
3716
+ *
3717
+ * @example
3718
+ * ```ts
3719
+ * const redis = new Redis({...})
3720
+ *
3721
+ * const script = redis.createScript<string>("return ARGV[1];")
3722
+ * const arg1 = await script.eval([], ["Hello World"])
3723
+ * expect(arg1, "Hello World")
3724
+ * ```
3725
+ * @example
3726
+ * ```ts
3727
+ * const redis = new Redis({...})
3728
+ *
3729
+ * const script = redis.createScript<string>("return ARGV[1];", { readonly: true })
3730
+ * const arg1 = await script.evalRo([], ["Hello World"])
3731
+ * expect(arg1, "Hello World")
3732
+ * ```
3733
+ */
3734
+ createScript(script, opts) {
3735
+ return opts?.readonly ? new ScriptRO(this, script) : new Script(this, script);
3191
3736
  }
3192
3737
  /**
3193
3738
  * Create a new pipeline that allows you to send requests in bulk.
@@ -3274,14 +3819,26 @@ var Redis = class {
3274
3819
  * @see https://redis.io/commands/echo
3275
3820
  */
3276
3821
  echo = (...args) => new EchoCommand(args, this.opts).exec(this.client);
3822
+ /**
3823
+ * @see https://redis.io/commands/eval_ro
3824
+ */
3825
+ evalRo = (...args) => new EvalROCommand(args, this.opts).exec(this.client);
3277
3826
  /**
3278
3827
  * @see https://redis.io/commands/eval
3279
3828
  */
3280
3829
  eval = (...args) => new EvalCommand(args, this.opts).exec(this.client);
3830
+ /**
3831
+ * @see https://redis.io/commands/evalsha_ro
3832
+ */
3833
+ evalshaRo = (...args) => new EvalshaROCommand(args, this.opts).exec(this.client);
3281
3834
  /**
3282
3835
  * @see https://redis.io/commands/evalsha
3283
3836
  */
3284
3837
  evalsha = (...args) => new EvalshaCommand(args, this.opts).exec(this.client);
3838
+ /**
3839
+ * Generic method to execute any Redis command.
3840
+ */
3841
+ exec = (args) => new ExecCommand(args, this.opts).exec(this.client);
3285
3842
  /**
3286
3843
  * @see https://redis.io/commands/exists
3287
3844
  */
@@ -3338,6 +3895,10 @@ var Redis = class {
3338
3895
  * @see https://redis.io/commands/getdel
3339
3896
  */
3340
3897
  getdel = (...args) => new GetDelCommand(args, this.opts).exec(this.client);
3898
+ /**
3899
+ * @see https://redis.io/commands/getex
3900
+ */
3901
+ getex = (...args) => new GetExCommand(args, this.opts).exec(this.client);
3341
3902
  /**
3342
3903
  * @see https://redis.io/commands/getrange
3343
3904
  */
@@ -3354,6 +3915,42 @@ var Redis = class {
3354
3915
  * @see https://redis.io/commands/hexists
3355
3916
  */
3356
3917
  hexists = (...args) => new HExistsCommand(args, this.opts).exec(this.client);
3918
+ /**
3919
+ * @see https://redis.io/commands/hexpire
3920
+ */
3921
+ hexpire = (...args) => new HExpireCommand(args, this.opts).exec(this.client);
3922
+ /**
3923
+ * @see https://redis.io/commands/hexpireat
3924
+ */
3925
+ hexpireat = (...args) => new HExpireAtCommand(args, this.opts).exec(this.client);
3926
+ /**
3927
+ * @see https://redis.io/commands/hexpiretime
3928
+ */
3929
+ hexpiretime = (...args) => new HExpireTimeCommand(args, this.opts).exec(this.client);
3930
+ /**
3931
+ * @see https://redis.io/commands/httl
3932
+ */
3933
+ httl = (...args) => new HTtlCommand(args, this.opts).exec(this.client);
3934
+ /**
3935
+ * @see https://redis.io/commands/hpexpire
3936
+ */
3937
+ hpexpire = (...args) => new HPExpireCommand(args, this.opts).exec(this.client);
3938
+ /**
3939
+ * @see https://redis.io/commands/hpexpireat
3940
+ */
3941
+ hpexpireat = (...args) => new HPExpireAtCommand(args, this.opts).exec(this.client);
3942
+ /**
3943
+ * @see https://redis.io/commands/hpexpiretime
3944
+ */
3945
+ hpexpiretime = (...args) => new HPExpireTimeCommand(args, this.opts).exec(this.client);
3946
+ /**
3947
+ * @see https://redis.io/commands/hpttl
3948
+ */
3949
+ hpttl = (...args) => new HPTtlCommand(args, this.opts).exec(this.client);
3950
+ /**
3951
+ * @see https://redis.io/commands/hpersist
3952
+ */
3953
+ hpersist = (...args) => new HPersistCommand(args, this.opts).exec(this.client);
3357
3954
  /**
3358
3955
  * @see https://redis.io/commands/hget
3359
3956
  */
@@ -3522,6 +4119,13 @@ var Redis = class {
3522
4119
  * @see https://redis.io/commands/psetex
3523
4120
  */
3524
4121
  psetex = (key, ttl, value) => new PSetEXCommand([key, ttl, value], this.opts).exec(this.client);
4122
+ /**
4123
+ * @see https://redis.io/commands/psubscribe
4124
+ */
4125
+ psubscribe = (patterns) => {
4126
+ const patternArray = Array.isArray(patterns) ? patterns : [patterns];
4127
+ return new Subscriber(this.client, patternArray, true);
4128
+ };
3525
4129
  /**
3526
4130
  * @see https://redis.io/commands/pttl
3527
4131
  */
@@ -3650,6 +4254,13 @@ var Redis = class {
3650
4254
  * @see https://redis.io/commands/strlen
3651
4255
  */
3652
4256
  strlen = (...args) => new StrLenCommand(args, this.opts).exec(this.client);
4257
+ /**
4258
+ * @see https://redis.io/commands/subscribe
4259
+ */
4260
+ subscribe = (channels) => {
4261
+ const channelArray = Array.isArray(channels) ? channels : [channels];
4262
+ return new Subscriber(this.client, channelArray);
4263
+ };
3653
4264
  /**
3654
4265
  * @see https://redis.io/commands/sunion
3655
4266
  */
@@ -3852,18 +4463,16 @@ var Redis2 = class extends Redis {
3852
4463
  console.warn(
3853
4464
  `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.`
3854
4465
  );
3855
- }
3856
- if (!config.token) {
4466
+ } else if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
3857
4467
  console.warn(
3858
- `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
4468
+ "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
3859
4469
  );
3860
4470
  }
3861
- if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) {
4471
+ if (!config.token) {
3862
4472
  console.warn(
3863
- "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!"
4473
+ `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.`
3864
4474
  );
3865
- }
3866
- if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
4475
+ } else if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) {
3867
4476
  console.warn(
3868
4477
  "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!"
3869
4478
  );