@upstash/redis 0.0.0-ci.f55d85c5 → 0.0.0-ci.f8d46c0e

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/README.md CHANGED
@@ -11,9 +11,9 @@ It is the only connectionless (HTTP based) Redis client and designed for:
11
11
 
12
12
  - Serverless functions (AWS Lambda ...)
13
13
  - Cloudflare Workers (see
14
- [the example](https://github.com/upstash/upstash-redis/tree/master/examples/cloudflare-workers))
14
+ [the example](https://github.com/upstash/upstash-redis/tree/main/examples/cloudflare-workers))
15
15
  - Fastly Compute@Edge (see
16
- [the example](https://github.com/upstash/upstash-redis/tree/master/examples/fastly))
16
+ [the example](https://github.com/upstash/upstash-redis/tree/main/examples/fastly))
17
17
  - Next.js, Jamstack ...
18
18
  - Client side web/mobile applications
19
19
  - WebAssembly
@@ -23,24 +23,6 @@ See
23
23
  [the list of APIs](https://docs.upstash.com/features/restapi#rest---redis-api-compatibility)
24
24
  supported.
25
25
 
26
- ## Upgrading to v1.4.0 **(ReferenceError: fetch is not defined)**
27
-
28
- If you are running on nodejs v17 and earlier, `fetch` will not be natively
29
- supported. Platforms like Vercel, Netlify, Deno, Fastly etc. provide a polyfill
30
- for you. But if you are running on bare node, you need to either specify a
31
- polyfill yourself or change the import path to:
32
-
33
- ```typescript
34
- import { Redis } from "@upstash/redis/with-fetch";
35
- ```
36
-
37
- ## Upgrading from v0.2.0?
38
-
39
- Please read the
40
- [migration guide](https://github.com/upstash/upstash-redis#migrating-to-v1). For
41
- further explanation we wrote a
42
- [blog post](https://blog.upstash.com/upstash-redis-sdk-v1).
43
-
44
26
  ## Quick Start
45
27
 
46
28
  ### Install
@@ -61,133 +43,7 @@ import { Redis } from "https://deno.land/x/upstash_redis/mod.ts";
61
43
 
62
44
  Create a new redis database on [upstash](https://console.upstash.com/)
63
45
 
64
- ### Environments
65
-
66
- We support various platforms, such as nodejs, cloudflare and fastly. Platforms
67
- differ slightly when it comes to environment variables and their `fetch` api.
68
- Please use the correct import when deploying to special platforms.
69
-
70
- #### Node.js
71
-
72
- Examples: Vercel, Netlify, AWS Lambda
73
-
74
- If you are running on nodejs you can set `UPSTASH_REDIS_REST_URL` and
75
- `UPSTASH_REDIS_REST_TOKEN` as environment variable and create a redis instance
76
- like this:
77
-
78
- ```ts
79
- import { Redis } from "@upstash/redis"
80
-
81
- const redis = new Redis({
82
- url: <UPSTASH_REDIS_REST_URL>,
83
- token: <UPSTASH_REDIS_REST_TOKEN>,
84
- })
85
-
86
- // or load directly from env
87
- const redis = Redis.fromEnv()
88
- ```
89
-
90
- If you are running on nodejs v17 and earlier, `fetch` will not be natively
91
- supported. Platforms like Vercel, Netlify, Deno, Fastly etc. provide a polyfill
92
- for you. But if you are running on bare node, you need to either specify a
93
- polyfill yourself or change the import path to:
94
-
95
- ```typescript
96
- import { Redis } from "@upstash/redis/with-fetch";
97
- ```
98
-
99
- - [Code example](https://github.com/upstash/upstash-redis/blob/main/examples/nodejs)
100
-
101
- #### Cloudflare Workers
102
-
103
- Cloudflare handles environment variables differently than nodejs. Please add
104
- `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` using
105
- `wrangler secret put ...` or in the cloudflare dashboard.
106
-
107
- Afterwards you can create a redis instance:
108
-
109
- ```ts
110
- import { Redis } from "@upstash/redis/cloudflare"
111
-
112
- const redis = new Redis({
113
- url: <UPSTASH_REDIS_REST_URL>,
114
- token: <UPSTASH_REDIS_REST_TOKEN>,
115
- })
116
-
117
-
118
- // or load directly from global env
119
-
120
- // service worker
121
- const redis = Redis.fromEnv()
122
-
123
-
124
- // module worker
125
- export default {
126
- async fetch(request: Request, env: Bindings) {
127
- const redis = Redis.fromEnv(env)
128
- // ...
129
- }
130
- }
131
- ```
132
-
133
- - [Code example service worker](https://github.com/upstash/upstash-redis/tree/main/examples/cloudflare-workers)
134
- - [Code example module worker](https://github.com/upstash/upstash-redis/tree/main/examples/cloudflare-workers-modules)
135
- - [Documentation](https://docs.upstash.com/redis/tutorials/cloudflare_workers_with_redis)
136
-
137
- #### Fastly
138
-
139
- Fastly introduces a concept called
140
- [backend](https://developer.fastly.com/reference/api/services/backend/). You
141
- need to configure a backend in your `fastly.toml`. An example can be found
142
- [here](https://github.com/upstash/upstash-redis/blob/main/examples/fastly/fastly.toml).
143
- Until the fastly api stabilizes we recommend creating an instance manually:
144
-
145
- ```ts
146
- import { Redis } from "@upstash/redis/fastly"
147
-
148
- const redis = new Redis({
149
- url: <UPSTASH_REDIS_REST_URL>,
150
- token: <UPSTASH_REDIS_REST_TOKEN>,
151
- backend: <BACKEND_NAME>,
152
- })
153
- ```
154
-
155
- - [Code example](https://github.com/upstash/upstash-redis/tree/main/examples/fastly)
156
- - [Documentation](https://blog.upstash.com/fastly-compute-edge-with-redi)
157
-
158
- #### Deno
159
-
160
- Examples: [Deno Deploy](https://deno.com/deploy),
161
- [Netlify Edge](https://www.netlify.com/products/edge/)
162
-
163
- ```ts
164
- import { Redis } from "https://deno.land/x/upstash_redis/mod.ts"
165
-
166
- const redis = new Redis({
167
- url: <UPSTASH_REDIS_REST_URL>,
168
- token: <UPSTASH_REDIS_REST_TOKEN>,
169
- })
170
-
171
- // or
172
- const redis = Redis.fromEnv();
173
- ```
174
-
175
- ### Working with types
176
-
177
- Most commands allow you to provide a type to make working with typescript
178
- easier.
179
-
180
- ```ts
181
- const data = await redis.get<MyCustomType>("key");
182
- // data is typed as `MyCustomType`
183
- ```
184
-
185
- ## Migrating to v1
186
-
187
- ### Explicit authentication
188
-
189
- The library is no longer automatically trying to load connection secrets from
190
- environment variables. You must either supply them yourself:
46
+ ## Basic Usage:
191
47
 
192
48
  ```ts
193
49
  import { Redis } from "@upstash/redis"
@@ -196,146 +52,52 @@ const redis = new Redis({
196
52
  url: <UPSTASH_REDIS_REST_URL>,
197
53
  token: <UPSTASH_REDIS_REST_TOKEN>,
198
54
  })
199
- ```
200
55
 
201
- Or use one of the static constructors to load from environment variables:
56
+ // string
57
+ await redis.set('key', 'value');
58
+ let data = await redis.get('key');
59
+ console.log(data)
202
60
 
203
- ```ts
204
- // Nodejs
205
- import { Redis } from "@upstash/redis";
206
- const redis = Redis.fromEnv();
207
- ```
61
+ await redis.set('key2', 'value2', {ex: 1});
208
62
 
209
- ```ts
210
- // or when deploying to cloudflare workers
211
- import { Redis } from "@upstash/redis/cloudflare";
212
- const redis = Redis.fromEnv();
213
- ```
63
+ // sorted set
64
+ await redis.zadd('scores', { score: 1, member: 'team1' })
65
+ data = await redis.zrange('scores', 0, 100 )
66
+ console.log(data)
214
67
 
215
- ### Error handling
68
+ // list
69
+ await redis.lpush('elements', 'magnesium')
70
+ data = await redis.lrange('elements', 0, 100 )
71
+ console.log(data)
216
72
 
217
- Errors are now thrown automatically instead of being returned to you.
73
+ // hash
74
+ await redis.hset('people', {name: 'joe'})
75
+ data = await redis.hget('people', 'name' )
76
+ console.log(data)
218
77
 
219
- ```ts
220
- // old
221
- const { data, error } = await set("key", "value");
222
- if (error) {
223
- throw new Error(error);
224
- }
225
-
226
- // new
227
- const data = await redis.set("key", "value"); // error is thrown automatically
78
+ // sets
79
+ await redis.sadd('animals', 'cat')
80
+ data = await redis.spop('animals', 1)
81
+ console.log(data)
228
82
  ```
229
83
 
230
- ## Pipeline
231
-
232
- `v1.0.0` introduces redis pipelines. Pipelining commands allows you to send a
233
- single http request with multiple commands.
234
-
235
- ```ts
236
- import { Redis } from "@upstash/redis";
237
-
238
- const redis = new Redis({
239
- /* auth */
240
- });
241
-
242
- const p = redis.pipeline();
84
+ ### Upgrading to v1.4.0 **(ReferenceError: fetch is not defined)**
243
85
 
244
- // Now you can chain multiple commands to create your pipeline:
245
-
246
- p.set("key", 2);
247
- p.incr("key");
248
-
249
- // or inline:
250
- p.hset("key2", "field", { hello: "world" }).hvals("key2");
251
-
252
- // Execute the pipeline once you are done building it:
253
- // `exec` returns an array where each element represents the response of a command in the pipeline.
254
- // You can optionally provide a type like this to get a typed response.
255
- const res = await p.exec<[Type1, Type2, Type3]>();
256
- ```
257
-
258
- For more information about pipelines using REST see
259
- [here](https://blog.upstash.com/pipeline).
260
-
261
- ### Advanced
262
-
263
- A low level `Command` class can be imported from `@upstash/redis` in case you
264
- need more control about types and or (de)serialization.
265
-
266
- By default all objects you are storing in redis are serialized using
267
- `JSON.stringify` and recursively deserialized as well. Here's an example how you
268
- could customize that behaviour. Keep in mind that you need to provide a `fetch`
269
- polyfill if you are running on nodejs. We recommend
270
- [isomorphic-fetch](https://www.npmjs.com/package/isomorphic-fetch).
271
-
272
- ```ts
273
- import { Command } from "@upstash/redis/commands"
274
- import { HttpClient } from "@upstash/redis/http"
275
-
276
- /**
277
- * TData represents what the user will enter or receive,
278
- * TResult is the raw data returned from upstash, which may need to be
279
- * transformed or parsed.
280
- */
281
- const deserialize: (raw: TResult) => TData = ...
282
-
283
- class CustomGetCommand<TData, TResult> extends Command<TData | null, TResult | null> {
284
- constructor(key: string, ) {
285
- super(["get", key], { deserialize })
286
- }
287
- }
288
-
289
- const client = new HttpClient({
290
- baseUrl: <UPSTASH_REDIS_REST_URL>,
291
- headers: {
292
- authorization: `Bearer ${<UPSTASH_REDIS_REST_TOKEN>}`,
293
- },
294
- })
295
-
296
- const res = new CustomGetCommand("key").exec(client)
297
- ```
298
-
299
- ### Additional information
300
-
301
- #### `keepalive`
302
-
303
- `@upstash/redis` is capable of reusing connections where possible to minimize
304
- latency. Connections can be reused if the client is stored in memory and not
305
- initialized with every new function invocation. The easiest way to achieve this
306
- is by creating the client outside of your handler and adding an https agent:
307
-
308
- ```ts
309
- // Nextjs api route
310
- import { Redis } from "@upstash/redis";
311
- import https from "https";
312
-
313
- const redis = Redis.fromEnv({
314
- agent: new https.Agent({ keepAlive: true }),
315
- });
86
+ If you are running on nodejs v17 and earlier, `fetch` will not be natively
87
+ supported. Platforms like Vercel, Netlify, Deno, Fastly etc. provide a polyfill
88
+ for you. But if you are running on bare node, you need to either specify a
89
+ polyfill yourself or change the import path to:
316
90
 
317
- export default async function (req, res) {
318
- // use redis here
319
- }
91
+ ```typescript
92
+ import { Redis } from "@upstash/redis/with-fetch";
320
93
  ```
321
94
 
322
- Whenever your hot lambda receives a new request the client is already
323
- initialized and the previously established connection to upstash is reused.
324
-
325
- #### Javascript MAX_SAFE_INTEGER
326
-
327
- Javascript can not handle numbers larger than `2^53 -1` safely and would return
328
- wrong results when trying to deserialize them. In these cases the default
329
- deserializer will return them as string instead. This might cause a mismatch
330
- with your custom types.
331
-
332
- ```ts
333
- await redis.set("key", "101600000000150081467");
334
- const res = await redis<number>("get");
335
- ```
95
+ ### Upgrading from v0.2.0?
336
96
 
337
- In this example `res` will still be a string despite the type annotation. Please
338
- keep that in mind and adjust accordingly.
97
+ Please read the
98
+ [migration guide](https://docs.upstash.com/redis/sdks/javascriptsdk/migration).
99
+ For further explanation we wrote a
100
+ [blog post](https://blog.upstash.com/upstash-redis-sdk-v1).
339
101
 
340
102
  ## Docs
341
103
 
@@ -5,16 +5,7 @@ import { Command } from "./command.js";
5
5
  export class ScriptExistsCommand extends Command {
6
6
  constructor(hashes, opts) {
7
7
  super(["script", "exists", ...hashes], {
8
- deserialize: (result) => {
9
- /**
10
- * This isn't very pretty but it does the job.
11
- * The user facing api is clean and will return a single `string` if they provided
12
- * a single script hash, and an array of strings of the same length when given an
13
- * array of hashes.
14
- */
15
- const parsed = result;
16
- return parsed.length === 1 ? parsed[0] : parsed;
17
- },
8
+ deserialize: (result) => result,
18
9
  ...opts,
19
10
  });
20
11
  }
package/esm/pkg/http.js CHANGED
@@ -19,14 +19,30 @@ export class HttpClient {
19
19
  writable: true,
20
20
  value: void 0
21
21
  });
22
+ Object.defineProperty(this, "retry", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
22
28
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
23
29
  this.headers = { "Content-Type": "application/json", ...config.headers };
24
- this.options = config.options;
30
+ this.options = { backend: config.options?.backend };
31
+ if (typeof config?.retry === "boolean" && config?.retry === false) {
32
+ this.retry = {
33
+ attempts: 1,
34
+ backoff: () => 0,
35
+ };
36
+ }
37
+ else {
38
+ this.retry = {
39
+ attempts: config?.retry?.retries ?? 5,
40
+ backoff: config?.retry?.backoff ??
41
+ ((retryCount) => Math.exp(retryCount) * 50),
42
+ };
43
+ }
25
44
  }
26
45
  async request(req) {
27
- if (!req.path) {
28
- req.path = [];
29
- }
30
46
  const requestOptions = {
31
47
  method: "POST",
32
48
  headers: this.headers,
@@ -37,9 +53,21 @@ export class HttpClient {
37
53
  */
38
54
  backend: this.options?.backend,
39
55
  };
40
- // fetch is defined by isomorphic fetch
41
- // eslint-disable-next-line no-undef
42
- const res = await fetch([this.baseUrl, ...req.path].join("/"), requestOptions);
56
+ let res = null;
57
+ let error = null;
58
+ for (let i = 0; i <= this.retry.attempts; i++) {
59
+ try {
60
+ res = await fetch([this.baseUrl, ...(req.path ?? [])].join("/"), requestOptions);
61
+ break;
62
+ }
63
+ catch (err) {
64
+ error = err;
65
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
66
+ }
67
+ }
68
+ if (!res) {
69
+ throw error ?? new Error("Exhausted all retries");
70
+ }
43
71
  const body = (await res.json());
44
72
  if (!res.ok) {
45
73
  throw new UpstashError(body.error);
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import { UpstashError } from "../pkg/error.js";
2
+ import { HttpClient } from "../pkg/http.js";
3
3
  /**
4
4
  * Serverless redis client for upstash.
5
5
  */
@@ -26,7 +26,8 @@ export class Redis extends core.Redis {
26
26
  /\r|\n/.test(config.token)) {
27
27
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
28
28
  }
29
- const client = defaultRequester({
29
+ const client = new HttpClient({
30
+ retry: config.retry,
30
31
  baseUrl: config.url,
31
32
  headers: { authorization: `Bearer ${config.token}` },
32
33
  });
@@ -44,9 +45,8 @@ export class Redis extends core.Redis {
44
45
  * ```ts
45
46
  * const redis = Redis.fromEnv(env)
46
47
  * ```
47
- *
48
48
  */
49
- static fromEnv(env) {
49
+ static fromEnv(env, opts) {
50
50
  // @ts-ignore These will be defined by cloudflare
51
51
  const url = env?.UPSTASH_REDIS_REST_URL ?? UPSTASH_REDIS_REST_URL;
52
52
  // @ts-ignore These will be defined by cloudflare
@@ -57,26 +57,6 @@ export class Redis extends core.Redis {
57
57
  if (!token) {
58
58
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_TOKEN`");
59
59
  }
60
- return new Redis({ url, token });
60
+ return new Redis({ ...opts, url, token });
61
61
  }
62
62
  }
63
- function defaultRequester(config) {
64
- return {
65
- request: async function (req) {
66
- if (!req.path) {
67
- req.path = [];
68
- }
69
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
70
- method: "POST",
71
- headers: { "Content-Type": "application/json", ...config.headers },
72
- body: JSON.stringify(req.body),
73
- keepalive: true,
74
- });
75
- const body = (await res.json());
76
- if (!res.ok) {
77
- throw new UpstashError(body.error);
78
- }
79
- return body;
80
- },
81
- };
82
- }
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import { UpstashError } from "../pkg/error.js";
2
+ import { HttpClient } from "../pkg/http.js";
3
3
  /**
4
4
  * Serverless redis client for upstash.
5
5
  */
@@ -27,35 +27,14 @@ export class Redis extends core.Redis {
27
27
  /\r|\n/.test(config.token)) {
28
28
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
29
29
  }
30
- const client = defaultRequester({
30
+ const client = new HttpClient({
31
31
  baseUrl: config.url,
32
+ retry: config.retry,
32
33
  headers: { authorization: `Bearer ${config.token}` },
33
- backend: config.backend,
34
+ options: { backend: config.backend },
34
35
  });
35
36
  super(client, {
36
37
  automaticDeserialization: config.automaticDeserialization,
37
38
  });
38
39
  }
39
40
  }
40
- function defaultRequester(config) {
41
- return {
42
- request: async function (req) {
43
- if (!req.path) {
44
- req.path = [];
45
- }
46
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
47
- method: "POST",
48
- headers: { "Content-Type": "application/json", ...config.headers },
49
- body: JSON.stringify(req.body),
50
- keepalive: true,
51
- // @ts-expect-error fastly requires `backend`
52
- backend: config.backend,
53
- });
54
- const body = (await res.json());
55
- if (!res.ok) {
56
- throw new UpstashError(body.error);
57
- }
58
- return body;
59
- },
60
- };
61
- }
@@ -1,6 +1,6 @@
1
1
  // deno-lint-ignore-file
2
2
  import * as core from "../pkg/redis.js";
3
- import { UpstashError } from "../pkg/error.js";
3
+ import { HttpClient, } from "../pkg/http.js";
4
4
  import "isomorphic-fetch";
5
5
  /**
6
6
  * Serverless redis client for upstash.
@@ -21,8 +21,9 @@ export class Redis extends core.Redis {
21
21
  /\r|\n/.test(configOrRequester.token)) {
22
22
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
23
23
  }
24
- const client = defaultRequester({
24
+ const client = new HttpClient({
25
25
  baseUrl: configOrRequester.url,
26
+ retry: configOrRequester.retry,
26
27
  headers: { authorization: `Bearer ${configOrRequester.token}` },
27
28
  // agent: configOrRequester.agent,
28
29
  });
@@ -54,28 +55,6 @@ export class Redis extends core.Redis {
54
55
  if (!token) {
55
56
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`");
56
57
  }
57
- return new Redis({ url, token, ...config });
58
+ return new Redis({ ...config, url, token });
58
59
  }
59
60
  }
60
- function defaultRequester(config) {
61
- return {
62
- request: async function (req) {
63
- if (!req.path) {
64
- req.path = [];
65
- }
66
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
67
- method: "POST",
68
- headers: { "Content-Type": "application/json", ...config.headers },
69
- body: JSON.stringify(req.body),
70
- keepalive: true,
71
- // @ts-ignore
72
- agent: config.agent,
73
- });
74
- const body = (await res.json());
75
- if (!res.ok) {
76
- throw new UpstashError(body.error);
77
- }
78
- return body;
79
- },
80
- };
81
- }
@@ -1,6 +1,6 @@
1
1
  // deno-lint-ignore-file
2
2
  import * as core from "../pkg/redis.js";
3
- import { UpstashError } from "../pkg/error.js";
3
+ import { HttpClient, } from "../pkg/http.js";
4
4
  /**
5
5
  * Serverless redis client for upstash.
6
6
  */
@@ -20,8 +20,9 @@ export class Redis extends core.Redis {
20
20
  /\r|\n/.test(configOrRequester.token)) {
21
21
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
22
22
  }
23
- const client = defaultRequester({
23
+ const client = new HttpClient({
24
24
  baseUrl: configOrRequester.url,
25
+ retry: configOrRequester.retry,
25
26
  headers: { authorization: `Bearer ${configOrRequester.token}` },
26
27
  // agent: configOrRequester.agent,
27
28
  });
@@ -53,28 +54,6 @@ export class Redis extends core.Redis {
53
54
  if (!token) {
54
55
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`");
55
56
  }
56
- return new Redis({ url, token, ...config });
57
+ return new Redis({ ...config, url, token });
57
58
  }
58
59
  }
59
- function defaultRequester(config) {
60
- return {
61
- request: async function (req) {
62
- if (!req.path) {
63
- req.path = [];
64
- }
65
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
66
- method: "POST",
67
- headers: { "Content-Type": "application/json", ...config.headers },
68
- body: JSON.stringify(req.body),
69
- keepalive: true,
70
- // @ts-ignore
71
- agent: config.agent,
72
- });
73
- const body = (await res.json());
74
- if (!res.ok) {
75
- throw new UpstashError(body.error);
76
- }
77
- return body;
78
- },
79
- };
80
- }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "main": "./script/platforms/nodejs.js",
4
4
  "types": "./types/platforms/nodejs.d.ts",
5
5
  "name": "@upstash/redis",
6
- "version": "v0.0.0-ci.f55d85c5",
6
+ "version": "v0.0.0-ci.f8d46c0e",
7
7
  "description": "An HTTP/REST based Redis client built on top of Upstash REST API.",
8
8
  "repository": {
9
9
  "type": "git",
@@ -16,36 +16,43 @@
16
16
  "edge",
17
17
  "upstash"
18
18
  ],
19
- "author": "Andreas Thomas <andreas.thomas@chronark.com>",
19
+ "author": "Andreas Thomas <dev@chronark.com>",
20
20
  "license": "MIT",
21
21
  "bugs": {
22
22
  "url": "https://github.com/upstash/upstash-redis/issues"
23
23
  },
24
- "dependencies": {
25
- "isomorphic-fetch": "^3.0.0"
26
- },
27
24
  "homepage": "https://github.com/upstash/upstash-redis#readme",
28
- "browser": {
29
- "isomorphic-fetch": false,
30
- "http": false,
31
- "https": false
32
- },
33
25
  "devDependencies": {
34
26
  "@size-limit/preset-small-lib": "latest",
35
27
  "size-limit": "latest"
36
28
  },
29
+ "dependencies": {
30
+ "isomorphic-fetch": "^3.0.0"
31
+ },
32
+ "typesVersions": {
33
+ "*": {
34
+ "nodejs": "./types/platforms/nodejs.d.ts",
35
+ "cloudflare": "./types/platforms/cloudflare.d.ts",
36
+ "fastly": "./types/platforms/fastly.d.ts",
37
+ "with-fetch": "./types/platforms/node_with_fetch.d.ts"
38
+ }
39
+ },
37
40
  "size-limit": [
38
41
  {
39
42
  "path": "esm/platforms/nodejs.js",
40
- "limit": "5 KB"
43
+ "limit": "6 KB"
41
44
  },
42
45
  {
43
46
  "path": "esm/platforms/fastly.js",
44
- "limit": "5 KB"
47
+ "limit": "6 KB"
45
48
  },
46
49
  {
47
50
  "path": "esm/platforms/cloudflare.js",
48
- "limit": "5 KB"
51
+ "limit": "6 KB"
52
+ },
53
+ {
54
+ "path": "esm/platforms/node_with_fetch.js",
55
+ "limit": "15 KB"
49
56
  },
50
57
  {
51
58
  "path": "script/platforms/nodejs.js",
@@ -58,6 +65,10 @@
58
65
  {
59
66
  "path": "script/platforms/cloudflare.js",
60
67
  "limit": "10 KB"
68
+ },
69
+ {
70
+ "path": "script/platforms/node_with_fetch.js",
71
+ "limit": "15 KB"
61
72
  }
62
73
  ],
63
74
  "exports": {
@@ -8,16 +8,7 @@ const command_js_1 = require("./command.js");
8
8
  class ScriptExistsCommand extends command_js_1.Command {
9
9
  constructor(hashes, opts) {
10
10
  super(["script", "exists", ...hashes], {
11
- deserialize: (result) => {
12
- /**
13
- * This isn't very pretty but it does the job.
14
- * The user facing api is clean and will return a single `string` if they provided
15
- * a single script hash, and an array of strings of the same length when given an
16
- * array of hashes.
17
- */
18
- const parsed = result;
19
- return parsed.length === 1 ? parsed[0] : parsed;
20
- },
11
+ deserialize: (result) => result,
21
12
  ...opts,
22
13
  });
23
14
  }
@@ -22,14 +22,30 @@ class HttpClient {
22
22
  writable: true,
23
23
  value: void 0
24
24
  });
25
+ Object.defineProperty(this, "retry", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: void 0
30
+ });
25
31
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
26
32
  this.headers = { "Content-Type": "application/json", ...config.headers };
27
- this.options = config.options;
33
+ this.options = { backend: config.options?.backend };
34
+ if (typeof config?.retry === "boolean" && config?.retry === false) {
35
+ this.retry = {
36
+ attempts: 1,
37
+ backoff: () => 0,
38
+ };
39
+ }
40
+ else {
41
+ this.retry = {
42
+ attempts: config?.retry?.retries ?? 5,
43
+ backoff: config?.retry?.backoff ??
44
+ ((retryCount) => Math.exp(retryCount) * 50),
45
+ };
46
+ }
28
47
  }
29
48
  async request(req) {
30
- if (!req.path) {
31
- req.path = [];
32
- }
33
49
  const requestOptions = {
34
50
  method: "POST",
35
51
  headers: this.headers,
@@ -40,9 +56,21 @@ class HttpClient {
40
56
  */
41
57
  backend: this.options?.backend,
42
58
  };
43
- // fetch is defined by isomorphic fetch
44
- // eslint-disable-next-line no-undef
45
- const res = await fetch([this.baseUrl, ...req.path].join("/"), requestOptions);
59
+ let res = null;
60
+ let error = null;
61
+ for (let i = 0; i <= this.retry.attempts; i++) {
62
+ try {
63
+ res = await fetch([this.baseUrl, ...(req.path ?? [])].join("/"), requestOptions);
64
+ break;
65
+ }
66
+ catch (err) {
67
+ error = err;
68
+ await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
69
+ }
70
+ }
71
+ if (!res) {
72
+ throw error ?? new Error("Exhausted all retries");
73
+ }
46
74
  const body = (await res.json());
47
75
  if (!res.ok) {
48
76
  throw new error_js_1.UpstashError(body.error);
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.Redis = void 0;
27
27
  const core = __importStar(require("../pkg/redis.js"));
28
- const error_js_1 = require("../pkg/error.js");
28
+ const http_js_1 = require("../pkg/http.js");
29
29
  /**
30
30
  * Serverless redis client for upstash.
31
31
  */
@@ -52,7 +52,8 @@ class Redis extends core.Redis {
52
52
  /\r|\n/.test(config.token)) {
53
53
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
54
54
  }
55
- const client = defaultRequester({
55
+ const client = new http_js_1.HttpClient({
56
+ retry: config.retry,
56
57
  baseUrl: config.url,
57
58
  headers: { authorization: `Bearer ${config.token}` },
58
59
  });
@@ -70,9 +71,8 @@ class Redis extends core.Redis {
70
71
  * ```ts
71
72
  * const redis = Redis.fromEnv(env)
72
73
  * ```
73
- *
74
74
  */
75
- static fromEnv(env) {
75
+ static fromEnv(env, opts) {
76
76
  // @ts-ignore These will be defined by cloudflare
77
77
  const url = env?.UPSTASH_REDIS_REST_URL ?? UPSTASH_REDIS_REST_URL;
78
78
  // @ts-ignore These will be defined by cloudflare
@@ -83,27 +83,7 @@ class Redis extends core.Redis {
83
83
  if (!token) {
84
84
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_TOKEN`");
85
85
  }
86
- return new Redis({ url, token });
86
+ return new Redis({ ...opts, url, token });
87
87
  }
88
88
  }
89
89
  exports.Redis = Redis;
90
- function defaultRequester(config) {
91
- return {
92
- request: async function (req) {
93
- if (!req.path) {
94
- req.path = [];
95
- }
96
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
97
- method: "POST",
98
- headers: { "Content-Type": "application/json", ...config.headers },
99
- body: JSON.stringify(req.body),
100
- keepalive: true,
101
- });
102
- const body = (await res.json());
103
- if (!res.ok) {
104
- throw new error_js_1.UpstashError(body.error);
105
- }
106
- return body;
107
- },
108
- };
109
- }
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.Redis = void 0;
27
27
  const core = __importStar(require("../pkg/redis.js"));
28
- const error_js_1 = require("../pkg/error.js");
28
+ const http_js_1 = require("../pkg/http.js");
29
29
  /**
30
30
  * Serverless redis client for upstash.
31
31
  */
@@ -53,10 +53,11 @@ class Redis extends core.Redis {
53
53
  /\r|\n/.test(config.token)) {
54
54
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
55
55
  }
56
- const client = defaultRequester({
56
+ const client = new http_js_1.HttpClient({
57
57
  baseUrl: config.url,
58
+ retry: config.retry,
58
59
  headers: { authorization: `Bearer ${config.token}` },
59
- backend: config.backend,
60
+ options: { backend: config.backend },
60
61
  });
61
62
  super(client, {
62
63
  automaticDeserialization: config.automaticDeserialization,
@@ -64,25 +65,3 @@ class Redis extends core.Redis {
64
65
  }
65
66
  }
66
67
  exports.Redis = Redis;
67
- function defaultRequester(config) {
68
- return {
69
- request: async function (req) {
70
- if (!req.path) {
71
- req.path = [];
72
- }
73
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
74
- method: "POST",
75
- headers: { "Content-Type": "application/json", ...config.headers },
76
- body: JSON.stringify(req.body),
77
- keepalive: true,
78
- // @ts-expect-error fastly requires `backend`
79
- backend: config.backend,
80
- });
81
- const body = (await res.json());
82
- if (!res.ok) {
83
- throw new error_js_1.UpstashError(body.error);
84
- }
85
- return body;
86
- },
87
- };
88
- }
@@ -26,7 +26,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.Redis = void 0;
28
28
  const core = __importStar(require("../pkg/redis.js"));
29
- const error_js_1 = require("../pkg/error.js");
29
+ const http_js_1 = require("../pkg/http.js");
30
30
  require("isomorphic-fetch");
31
31
  /**
32
32
  * Serverless redis client for upstash.
@@ -47,8 +47,9 @@ class Redis extends core.Redis {
47
47
  /\r|\n/.test(configOrRequester.token)) {
48
48
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
49
49
  }
50
- const client = defaultRequester({
50
+ const client = new http_js_1.HttpClient({
51
51
  baseUrl: configOrRequester.url,
52
+ retry: configOrRequester.retry,
52
53
  headers: { authorization: `Bearer ${configOrRequester.token}` },
53
54
  // agent: configOrRequester.agent,
54
55
  });
@@ -80,29 +81,7 @@ class Redis extends core.Redis {
80
81
  if (!token) {
81
82
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`");
82
83
  }
83
- return new Redis({ url, token, ...config });
84
+ return new Redis({ ...config, url, token });
84
85
  }
85
86
  }
86
87
  exports.Redis = Redis;
87
- function defaultRequester(config) {
88
- return {
89
- request: async function (req) {
90
- if (!req.path) {
91
- req.path = [];
92
- }
93
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
94
- method: "POST",
95
- headers: { "Content-Type": "application/json", ...config.headers },
96
- body: JSON.stringify(req.body),
97
- keepalive: true,
98
- // @ts-ignore
99
- agent: config.agent,
100
- });
101
- const body = (await res.json());
102
- if (!res.ok) {
103
- throw new error_js_1.UpstashError(body.error);
104
- }
105
- return body;
106
- },
107
- };
108
- }
@@ -26,7 +26,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.Redis = void 0;
28
28
  const core = __importStar(require("../pkg/redis.js"));
29
- const error_js_1 = require("../pkg/error.js");
29
+ const http_js_1 = require("../pkg/http.js");
30
30
  /**
31
31
  * Serverless redis client for upstash.
32
32
  */
@@ -46,8 +46,9 @@ class Redis extends core.Redis {
46
46
  /\r|\n/.test(configOrRequester.token)) {
47
47
  console.warn("The redis token contains whitespace or newline, which can cause errors!");
48
48
  }
49
- const client = defaultRequester({
49
+ const client = new http_js_1.HttpClient({
50
50
  baseUrl: configOrRequester.url,
51
+ retry: configOrRequester.retry,
51
52
  headers: { authorization: `Bearer ${configOrRequester.token}` },
52
53
  // agent: configOrRequester.agent,
53
54
  });
@@ -79,29 +80,7 @@ class Redis extends core.Redis {
79
80
  if (!token) {
80
81
  throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`");
81
82
  }
82
- return new Redis({ url, token, ...config });
83
+ return new Redis({ ...config, url, token });
83
84
  }
84
85
  }
85
86
  exports.Redis = Redis;
86
- function defaultRequester(config) {
87
- return {
88
- request: async function (req) {
89
- if (!req.path) {
90
- req.path = [];
91
- }
92
- const res = await fetch([config.baseUrl, ...req.path].join("/"), {
93
- method: "POST",
94
- headers: { "Content-Type": "application/json", ...config.headers },
95
- body: JSON.stringify(req.body),
96
- keepalive: true,
97
- // @ts-ignore
98
- agent: config.agent,
99
- });
100
- const body = (await res.json());
101
- if (!res.ok) {
102
- throw new error_js_1.UpstashError(body.error);
103
- }
104
- return body;
105
- },
106
- };
107
- }
@@ -2,8 +2,8 @@ import { Command, CommandOptions } from "./command.js";
2
2
  /**
3
3
  * @see https://redis.io/commands/hmset
4
4
  */
5
- export declare class HMSetCommand<TData> extends Command<number, number> {
5
+ export declare class HMSetCommand<TData> extends Command<"OK", "OK"> {
6
6
  constructor([key, kv]: [key: string, kv: {
7
7
  [field: string]: TData;
8
- }], opts?: CommandOptions<number, number>);
8
+ }], opts?: CommandOptions<"OK", "OK">);
9
9
  }
@@ -1,9 +1,7 @@
1
1
  import { Command, CommandOptions } from "./command.js";
2
- declare type TupleOfLength<T, L extends number, R extends T[] = []> = R["length"] extends L ? R : TupleOfLength<T, L, [...R, T]>;
3
2
  /**
4
3
  * @see https://redis.io/commands/script-exists
5
4
  */
6
- export declare class ScriptExistsCommand<T extends [string, ...string[]]> extends Command<T extends [string] ? string : TupleOfLength<string, T["length"]>, TupleOfLength<string, T["length"]>> {
7
- constructor(hashes: T, opts?: CommandOptions<T extends [string] ? string : TupleOfLength<string, T["length"]>, TupleOfLength<string, T["length"]>>);
5
+ export declare class ScriptExistsCommand<T extends string[]> extends Command<string[], number[]> {
6
+ constructor(hashes: T, opts?: CommandOptions<string[], number[]>);
8
7
  }
9
- export {};
@@ -2,6 +2,6 @@ import { Command, CommandOptions } from "./command.js";
2
2
  /**
3
3
  * @see https://redis.io/commands/sinterstore
4
4
  */
5
- export declare class SInterStoreCommand<TData = string> extends Command<unknown[], TData[]> {
6
- constructor(cmd: [destination: string, key: string, ...keys: string[]], opts?: CommandOptions<unknown[], TData[]>);
5
+ export declare class SInterStoreCommand extends Command<number, number> {
6
+ constructor(cmd: [destination: string, key: string, ...keys: string[]], opts?: CommandOptions<number, number>);
7
7
  }
@@ -2,6 +2,6 @@ import { Command, CommandOptions } from "./command.js";
2
2
  /**
3
3
  * @see https://redis.io/commands/spop
4
4
  */
5
- export declare class SPopCommand<TData = number> extends Command<string | null, TData | null> {
5
+ export declare class SPopCommand<TData> extends Command<string | null, TData | null> {
6
6
  constructor([key, count]: [key: string, count?: number], opts?: CommandOptions<string | null, TData | null>);
7
7
  }
@@ -12,12 +12,31 @@ export declare type UpstashResponse<TResult> = {
12
12
  export interface Requester {
13
13
  request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;
14
14
  }
15
+ export declare type RetryConfig = false | {
16
+ /**
17
+ * The number of retries to attempt before giving up.
18
+ *
19
+ * @default 5
20
+ */
21
+ retries?: number;
22
+ /**
23
+ * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.
24
+ *
25
+ * @default
26
+ * ```ts
27
+ * Math.exp(retryCount) * 50
28
+ * ```
29
+ */
30
+ backoff?: (retryCount: number) => number;
31
+ };
32
+ declare type Options = {
33
+ backend?: string;
34
+ };
15
35
  export declare type HttpClientConfig = {
16
36
  headers?: Record<string, string>;
17
37
  baseUrl: string;
18
- options?: {
19
- backend?: string;
20
- };
38
+ options?: Options;
39
+ retry?: RetryConfig;
21
40
  };
22
41
  export declare class HttpClient implements Requester {
23
42
  baseUrl: string;
@@ -25,6 +44,11 @@ export declare class HttpClient implements Requester {
25
44
  readonly options?: {
26
45
  backend?: string;
27
46
  };
47
+ readonly retry: {
48
+ attempts: number;
49
+ backoff: (retryCount: number) => number;
50
+ };
28
51
  constructor(config: HttpClientConfig);
29
52
  request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>>;
30
53
  }
54
+ export {};
@@ -1,4 +1,4 @@
1
- import { DelCommand, ExistsCommand, FlushAllCommand, PingCommand, ScoreMember, SetCommandOptions, TouchCommand, UnlinkCommand, ZAddCommandOptions, ZAddCommandOptionsWithIncr, ZRangeCommandOptions } from "./commands/mod.js";
1
+ import { DelCommand, ExistsCommand, FlushAllCommand, PingCommand, ScoreMember, ScriptExistsCommand, SetCommandOptions, TouchCommand, UnlinkCommand, ZAddCommandOptions, ZAddCommandOptionsWithIncr, ZRangeCommandOptions } from "./commands/mod.js";
2
2
  import { CommandOptions } from "./commands/command.js";
3
3
  import { Requester } from "./http.js";
4
4
  import { CommandArgs } from "./types.js";
@@ -349,7 +349,7 @@ export declare class Pipeline {
349
349
  /**
350
350
  * @see https://redis.io/commands/script-exists
351
351
  */
352
- scriptExists: (args_0: string, ...args_1: string[]) => this;
352
+ scriptExists: (...args: CommandArgs<typeof ScriptExistsCommand>) => this;
353
353
  /**
354
354
  * @see https://redis.io/commands/script-flush
355
355
  */
@@ -1,4 +1,4 @@
1
- import { CommandOptions, DelCommand, ExistsCommand, FlushAllCommand, PingCommand, ScoreMember, SetCommandOptions, TouchCommand, UnlinkCommand, ZAddCommandOptions, ZAddCommandOptionsWithIncr, ZRangeCommandOptions } from "./commands/mod.js";
1
+ import { CommandOptions, DelCommand, ExistsCommand, FlushAllCommand, PingCommand, ScoreMember, ScriptExistsCommand, SetCommandOptions, TouchCommand, UnlinkCommand, ZAddCommandOptions, ZAddCommandOptionsWithIncr, ZRangeCommandOptions } from "./commands/mod.js";
2
2
  import { Requester } from "./http.js";
3
3
  import { Pipeline } from "./pipeline.js";
4
4
  import type { CommandArgs } from "./types.js";
@@ -160,7 +160,7 @@ export declare class Redis {
160
160
  */
161
161
  hmset: <TData>(key: string, kv: {
162
162
  [field: string]: TData;
163
- }) => Promise<number>;
163
+ }) => Promise<"OK">;
164
164
  /**
165
165
  * @see https://redis.io/commands/hscan
166
166
  */
@@ -322,7 +322,7 @@ export declare class Redis {
322
322
  /**
323
323
  * @see https://redis.io/commands/script-exists
324
324
  */
325
- scriptExists: (args_0: string, ...args_1: string[]) => Promise<[]>;
325
+ scriptExists: (...args: CommandArgs<typeof ScriptExistsCommand>) => Promise<number[]>;
326
326
  /**
327
327
  * @see https://redis.io/commands/script-flush
328
328
  */
@@ -366,7 +366,7 @@ export declare class Redis {
366
366
  /**
367
367
  * @see https://redis.io/commands/sinterstore
368
368
  */
369
- sinterstore: (destination: string, key: string, ...keys: string[]) => Promise<string[]>;
369
+ sinterstore: (destination: string, key: string, ...keys: string[]) => Promise<number>;
370
370
  /**
371
371
  * @see https://redis.io/commands/sismember
372
372
  */
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import type { Requester, UpstashRequest, UpstashResponse } from "../pkg/http.js";
2
+ import type { Requester, RetryConfig, UpstashRequest, UpstashResponse } from "../pkg/http.js";
3
3
  export type { Requester, UpstashRequest, UpstashResponse };
4
4
  /**
5
5
  * Connection credentials for upstash redis.
@@ -14,6 +14,10 @@ export declare type RedisConfigCloudflare = {
14
14
  * UPSTASH_REDIS_REST_TOKEN
15
15
  */
16
16
  token: string;
17
+ /**
18
+ * Configure the retry behaviour in case of network errors
19
+ */
20
+ retry?: RetryConfig;
17
21
  } & core.RedisOptions;
18
22
  /**
19
23
  * Serverless redis client for upstash.
@@ -34,5 +38,5 @@ export declare class Redis extends core.Redis {
34
38
  static fromEnv(env?: {
35
39
  UPSTASH_REDIS_REST_URL: string;
36
40
  UPSTASH_REDIS_REST_TOKEN: string;
37
- }): Redis;
41
+ }, opts?: Omit<RedisConfigCloudflare, "url" | "token">): Redis;
38
42
  }
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import type { Requester, UpstashRequest, UpstashResponse } from "../pkg/http.js";
2
+ import type { Requester, RetryConfig, UpstashRequest, UpstashResponse } from "../pkg/http.js";
3
3
  export type { Requester, UpstashRequest, UpstashResponse };
4
4
  /**
5
5
  * Connection credentials for upstash redis.
@@ -20,6 +20,10 @@ export declare type RedisConfigFastly = {
20
20
  * referenced by name.
21
21
  */
22
22
  backend: string;
23
+ /**
24
+ * Configure the retry behaviour in case of network errors
25
+ */
26
+ retry?: RetryConfig;
23
27
  } & core.RedisOptions;
24
28
  /**
25
29
  * Serverless redis client for upstash.
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import { Requester, UpstashRequest, UpstashResponse } from "../pkg/http.js";
2
+ import { Requester, RetryConfig, UpstashRequest, UpstashResponse } from "../pkg/http.js";
3
3
  import "isomorphic-fetch";
4
4
  export type { Requester, UpstashRequest, UpstashResponse };
5
5
  /**
@@ -15,6 +15,25 @@ export declare type RedisConfigNodejs = {
15
15
  * UPSTASH_REDIS_REST_TOKEN
16
16
  */
17
17
  token: string;
18
+ /**
19
+ * An agent allows you to reuse connections to reduce latency for multiple sequential requests.
20
+ *
21
+ * This is a node specific implementation and is not supported in various runtimes like Vercel
22
+ * edge functions.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import https from "https"
27
+ *
28
+ * const options: RedisConfigNodejs = {
29
+ * agent: new https.Agent({ keepAlive: true })
30
+ * }
31
+ * ```
32
+ */
33
+ /**
34
+ * Configure the retry behaviour in case of network errors
35
+ */
36
+ retry?: RetryConfig;
18
37
  } & core.RedisOptions;
19
38
  /**
20
39
  * Serverless redis client for upstash.
@@ -1,5 +1,5 @@
1
1
  import * as core from "../pkg/redis.js";
2
- import { Requester, UpstashRequest, UpstashResponse } from "../pkg/http.js";
2
+ import { Requester, RetryConfig, UpstashRequest, UpstashResponse } from "../pkg/http.js";
3
3
  export type { Requester, UpstashRequest, UpstashResponse };
4
4
  /**
5
5
  * Connection credentials for upstash redis.
@@ -14,6 +14,25 @@ export declare type RedisConfigNodejs = {
14
14
  * UPSTASH_REDIS_REST_TOKEN
15
15
  */
16
16
  token: string;
17
+ /**
18
+ * An agent allows you to reuse connections to reduce latency for multiple sequential requests.
19
+ *
20
+ * This is a node specific implementation and is not supported in various runtimes like Vercel
21
+ * edge functions.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import https from "https"
26
+ *
27
+ * const options: RedisConfigNodejs = {
28
+ * agent: new https.Agent({ keepAlive: true })
29
+ * }
30
+ * ```
31
+ */
32
+ /**
33
+ * Configure the retry behaviour in case of network errors
34
+ */
35
+ retry?: RetryConfig;
17
36
  } & core.RedisOptions;
18
37
  /**
19
38
  * Serverless redis client for upstash.