From 8433f4234b173ccf39e39321ccdc054d9531dfa4 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 11:57:46 +0300 Subject: [PATCH 1/9] fix: add errors to exports --- platforms/cloudflare.ts | 1 + platforms/fastly.ts | 1 + platforms/nodejs.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/platforms/cloudflare.ts b/platforms/cloudflare.ts index f75f60c5..35881664 100644 --- a/platforms/cloudflare.ts +++ b/platforms/cloudflare.ts @@ -8,6 +8,7 @@ type Env = { UPSTASH_DISABLE_TELEMETRY?: string; }; +export * as errors from "../pkg/error" export type * from "../pkg/commands/types"; export type { Requester, UpstashRequest, UpstashResponse, Pipeline }; /** diff --git a/platforms/fastly.ts b/platforms/fastly.ts index 8bd520bf..6dcef11a 100644 --- a/platforms/fastly.ts +++ b/platforms/fastly.ts @@ -4,6 +4,7 @@ import { HttpClient } from "../pkg/http"; import * as core from "../pkg/redis"; import { VERSION } from "../version"; +export * as errors from "../pkg/error" export type * from "../pkg/commands/types"; export type { Requester, UpstashRequest, UpstashResponse, Pipeline }; diff --git a/platforms/nodejs.ts b/platforms/nodejs.ts index 54080e99..7bf7d8a1 100644 --- a/platforms/nodejs.ts +++ b/platforms/nodejs.ts @@ -17,6 +17,7 @@ import { VERSION } from "../version"; if (typeof atob === "undefined") { global.atob = (b64: string) => Buffer.from(b64, "base64").toString("utf-8"); } +export * as errors from "../pkg/error" export type * from "../pkg/commands/types"; export type { Requester, UpstashRequest, UpstashResponse, Pipeline }; From 2ff1faf36ca1dec11a904d16662b460a96a2b964 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 11:59:56 +0300 Subject: [PATCH 2/9] feat: validate url for https --- pkg/error.ts | 7 +++++++ pkg/http.test.ts | 25 +++++++++++++++++++++++++ pkg/http.ts | 6 +++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/error.ts b/pkg/error.ts index f2b194f7..cef119c4 100644 --- a/pkg/error.ts +++ b/pkg/error.ts @@ -7,3 +7,10 @@ export class UpstashError extends Error { this.name = "UpstashError"; } } + +export class UrlError extends Error { + constructor(url: string) { + super(`Received an invalid URL: "${url}"`); + this.name = "UrlError"; + } +} \ No newline at end of file diff --git a/pkg/http.test.ts b/pkg/http.test.ts index 3ed1c76a..e1eb8fa7 100644 --- a/pkg/http.test.ts +++ b/pkg/http.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test"; import { HttpClient } from "./http"; import { newHttpClient } from "./test-utils"; +import { UrlError } from "./error"; test("remove trailing slash from urls", () => { const client = new HttpClient({ baseUrl: "https://example.com/" }); @@ -48,3 +49,27 @@ describe("Abort", () => { expect((await body).result).toEqual("Abort works!"); }); }); + +describe("should reject invalid urls", () => { + test("should reject when https is missing", async () => { + try { + new HttpClient({baseUrl: "eu1-flying-whale-434343.upstash.io"}) + } catch (error) { + expect(error instanceof UrlError).toBeTrue() + return + } + throw new Error("Test error. Should have raised when initializing client") + }) + + test("should allow when http is used", async () => { + new HttpClient({ + baseUrl: "http://eu1-flying-whale-434343.upstash.io", + }) + }) + + test("should allow when https is used", async () => { + new HttpClient({ + baseUrl: "https://eu1-flying-whale-434343.upstash.io", + }) + }) +}) \ No newline at end of file diff --git a/pkg/http.ts b/pkg/http.ts index dc98e3c6..94991281 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -1,4 +1,4 @@ -import { UpstashError } from "./error"; +import { UpstashError, UrlError } from "./error"; import { Telemetry } from "./types"; type CacheSetting = @@ -125,6 +125,10 @@ export class HttpClient implements Requester { }; this.baseUrl = config.baseUrl.replace(/\/$/, ""); + const urlRegex = /^https?:\/\/[^\s/$.?#].[^\s]*$/; + if (!urlRegex.test(this.baseUrl)) { + throw new UrlError(this.baseUrl) + } this.headers = { "Content-Type": "application/json", From 7c77271ca5bc88658eb608d7c9ffc09c9cb2b1e9 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 12:09:34 +0300 Subject: [PATCH 3/9] fix: make url error more verbose --- pkg/error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/error.ts b/pkg/error.ts index cef119c4..84dcd36f 100644 --- a/pkg/error.ts +++ b/pkg/error.ts @@ -10,7 +10,7 @@ export class UpstashError extends Error { export class UrlError extends Error { constructor(url: string) { - super(`Received an invalid URL: "${url}"`); + super(`Upstash Redis client was passed an invalid URL: "${url}"`); this.name = "UrlError"; } } \ No newline at end of file From a715b0abad4ae01a2d4499e5d8aacf8ec787a201 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 12:18:57 +0300 Subject: [PATCH 4/9] fix: fmt --- pkg/auto-pipeline.test.ts | 56 ++++++++++++++------------------------- pkg/auto-pipeline.ts | 16 +++++------ pkg/commands/scan.ts | 11 +++----- pkg/commands/sscan.ts | 11 +++----- pkg/commands/zscan.ts | 11 +++----- pkg/error.ts | 2 +- pkg/http.test.ts | 28 ++++++++++---------- pkg/http.ts | 8 +++--- pkg/redis.ts | 2 +- pkg/types.ts | 2 +- pkg/util.ts | 6 ++--- 11 files changed, 64 insertions(+), 89 deletions(-) diff --git a/pkg/auto-pipeline.test.ts b/pkg/auto-pipeline.test.ts index 34120530..335e4888 100644 --- a/pkg/auto-pipeline.test.ts +++ b/pkg/auto-pipeline.test.ts @@ -17,7 +17,7 @@ describe("Auto pipeline", () => { const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -152,10 +152,9 @@ describe("Auto pipeline", () => { }); test("should group async requests with sync requests", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -178,10 +177,9 @@ describe("Auto pipeline", () => { }); test("should execute a pipeline for each consecutive awaited command", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -204,10 +202,9 @@ describe("Auto pipeline", () => { }); test("should execute a single pipeline for several commands inside Promise.all", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -222,14 +219,12 @@ describe("Auto pipeline", () => { // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(1); expect(resArray).toEqual(["OK", 1, 2, "OK", "bar"]); - }); test("should be able to utilize only redis functions 'use' like usual", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -239,7 +234,7 @@ describe("Auto pipeline", () => { state = true; return await next(req); }); - + // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -252,10 +247,9 @@ describe("Auto pipeline", () => { }); test("should be able to utilize only redis functions 'multi' and 'pipeline' like usual", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); @@ -263,48 +257,46 @@ describe("Auto pipeline", () => { const pipe = redis.pipeline(); pipe.incr("voila"); pipe.incr("voila"); - const result = await pipe.exec() - expect(result).toEqual([1, 2]) - + const result = await pipe.exec(); + expect(result).toEqual([1, 2]); + // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); const transaction = redis.multi(); transaction.incr("et voila"); transaction.incr("et voila"); - const result_2 = await transaction.exec() - expect(result_2).toEqual([1, 2]) - + const result_2 = await transaction.exec(); + expect(result_2).toEqual([1, 2]); + // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); }); test("should be able to utilize only redis functions 'createScript' like usual", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); const script = redis.createScript("return ARGV[1];"); - + // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(0); const res = await script.eval([], ["Hello World"]); expect(res).toEqual("Hello World"); - + // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(1); }); test("should handle JSON commands correctly", async () => { - const redis = Redis.fromEnv({ latencyLogging: false, - enableAutoPipelining: true + enableAutoPipelining: true, }); // @ts-expect-error pipelineCounter is not in type but accessible @@ -317,19 +309,11 @@ describe("Auto pipeline", () => { redis.json.get("baz1"), redis.json.del("baz1"), redis.json.get("baz1"), - ]) + ]); // @ts-expect-error pipelineCounter is not in type but accessible expect(redis.pipelineCounter).toBe(1); - expect(res).toEqual([ - "OK", - "OK", - "bar", - { hello: "world" }, - 1, - null - ]) - }) + expect(res).toEqual(["OK", "OK", "bar", { hello: "world" }, 1, null]); + }); }); - diff --git a/pkg/auto-pipeline.ts b/pkg/auto-pipeline.ts index 8489f716..18731a39 100644 --- a/pkg/auto-pipeline.ts +++ b/pkg/auto-pipeline.ts @@ -16,7 +16,7 @@ export function createAutoPipelineProxy(_redis: Redis, json?: boolean): Redis { } return new Proxy(redis, { - get: (redis, command: "pipelineCounter" | keyof Pipeline | redisOnly ) => { + get: (redis, command: "pipelineCounter" | keyof Pipeline | redisOnly) => { // return pipelineCounter of autoPipelineExecutor if (command === "pipelineCounter") { return redis.autoPipelineExecutor.pipelineCounter; @@ -24,13 +24,13 @@ export function createAutoPipelineProxy(_redis: Redis, json?: boolean): Redis { if (command === "json") { return createAutoPipelineProxy(redis, true); - }; - + } + const commandInRedisButNotPipeline = command in redis && !(command in redis.autoPipelineExecutor.pipeline); if (commandInRedisButNotPipeline) { - return redis[command as redisOnly]; + return redis[command as redisOnly]; } // If the method is a function on the pipeline, wrap it with the executor logic @@ -39,7 +39,7 @@ export function createAutoPipelineProxy(_redis: Redis, json?: boolean): Redis { // pass the function as a callback return redis.autoPipelineExecutor.withAutoPipeline((pipeline) => { if (json) { - (pipeline.json[command as keyof Pipeline["json"]] as Function)(...args) + (pipeline.json[command as keyof Pipeline["json"]] as Function)(...args); } else { (pipeline[command as keyof Pipeline] as Function)(...args); } @@ -94,7 +94,7 @@ class AutoPipelineExecutor { } private async deferExecution() { - await Promise.resolve() - return await Promise.resolve() + await Promise.resolve(); + return await Promise.resolve(); } -} \ No newline at end of file +} diff --git a/pkg/commands/scan.ts b/pkg/commands/scan.ts index 1fe4ff03..dea9e8b9 100644 --- a/pkg/commands/scan.ts +++ b/pkg/commands/scan.ts @@ -24,12 +24,9 @@ export class ScanCommand extends Command<[string, string[]], [string, string[]]> if (opts?.type && opts.type.length > 0) { command.push("type", opts.type); } - super( - command, - { - deserialize: deserializeScanResponse, - ...cmdOpts, - } - ); + super(command, { + deserialize: deserializeScanResponse, + ...cmdOpts, + }); } } diff --git a/pkg/commands/sscan.ts b/pkg/commands/sscan.ts index 02d4122f..310b636d 100644 --- a/pkg/commands/sscan.ts +++ b/pkg/commands/sscan.ts @@ -21,12 +21,9 @@ export class SScanCommand extends Command< command.push("count", opts.count); } - super( - command, - { - deserialize: deserializeScanResponse, - ...cmdOpts, - } - ); + super(command, { + deserialize: deserializeScanResponse, + ...cmdOpts, + }); } } diff --git a/pkg/commands/zscan.ts b/pkg/commands/zscan.ts index e43f6d8e..75c7b30a 100644 --- a/pkg/commands/zscan.ts +++ b/pkg/commands/zscan.ts @@ -21,12 +21,9 @@ export class ZScanCommand extends Command< command.push("count", opts.count); } - super( - command, - { - deserialize: deserializeScanResponse, - ...cmdOpts, - } - ); + super(command, { + deserialize: deserializeScanResponse, + ...cmdOpts, + }); } } diff --git a/pkg/error.ts b/pkg/error.ts index 84dcd36f..c6d90252 100644 --- a/pkg/error.ts +++ b/pkg/error.ts @@ -13,4 +13,4 @@ export class UrlError extends Error { super(`Upstash Redis client was passed an invalid URL: "${url}"`); this.name = "UrlError"; } -} \ No newline at end of file +} diff --git a/pkg/http.test.ts b/pkg/http.test.ts index e1eb8fa7..56d4816f 100644 --- a/pkg/http.test.ts +++ b/pkg/http.test.ts @@ -1,8 +1,8 @@ import { describe, expect, test } from "bun:test"; import { HttpClient } from "./http"; -import { newHttpClient } from "./test-utils"; import { UrlError } from "./error"; +import { newHttpClient } from "./test-utils"; test("remove trailing slash from urls", () => { const client = new HttpClient({ baseUrl: "https://example.com/" }); @@ -51,25 +51,25 @@ describe("Abort", () => { }); describe("should reject invalid urls", () => { - test("should reject when https is missing", async () => { + test("should reject when https is missing", () => { try { - new HttpClient({baseUrl: "eu1-flying-whale-434343.upstash.io"}) + new HttpClient({ baseUrl: "eu1-flying-whale-434343.upstash.io" }); } catch (error) { - expect(error instanceof UrlError).toBeTrue() - return + expect(error instanceof UrlError).toBeTrue(); + return; } - throw new Error("Test error. Should have raised when initializing client") - }) + throw new Error("Test error. Should have raised when initializing client"); + }); - test("should allow when http is used", async () => { + test("should allow when http is used", () => { new HttpClient({ baseUrl: "http://eu1-flying-whale-434343.upstash.io", - }) - }) + }); + }); - test("should allow when https is used", async () => { + test("should allow when https is used", () => { new HttpClient({ baseUrl: "https://eu1-flying-whale-434343.upstash.io", - }) - }) -}) \ No newline at end of file + }); + }); +}); diff --git a/pkg/http.ts b/pkg/http.ts index 94991281..84b3fc7e 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -94,7 +94,7 @@ export type HttpClientConfig = { retry?: RetryConfig; agent?: any; signal?: AbortSignal; - keepAlive?: boolean + keepAlive?: boolean; } & RequesterConfig; export class HttpClient implements Requester { @@ -106,7 +106,7 @@ export class HttpClient implements Requester { signal?: AbortSignal; responseEncoding?: false | "base64"; cache?: CacheSetting; - keepAlive: boolean + keepAlive: boolean; }; public readonly retry: { @@ -121,13 +121,13 @@ export class HttpClient implements Requester { responseEncoding: config.responseEncoding ?? "base64", // default to base64 cache: config.cache, signal: config.signal, - keepAlive: config.keepAlive ?? true + keepAlive: config.keepAlive ?? true, }; this.baseUrl = config.baseUrl.replace(/\/$/, ""); const urlRegex = /^https?:\/\/[^\s/$.?#].[^\s]*$/; if (!urlRegex.test(this.baseUrl)) { - throw new UrlError(this.baseUrl) + throw new UrlError(this.baseUrl); } this.headers = { diff --git a/pkg/redis.ts b/pkg/redis.ts index 111f5448..dbf3e1e6 100644 --- a/pkg/redis.ts +++ b/pkg/redis.ts @@ -1,3 +1,4 @@ +import { createAutoPipelineProxy } from "../pkg/auto-pipeline"; import { AppendCommand, BitCountCommand, @@ -176,7 +177,6 @@ import { Requester, UpstashRequest, UpstashResponse } from "./http"; import { Pipeline } from "./pipeline"; import { Script } from "./script"; import type { CommandArgs, RedisOptions, Telemetry } from "./types"; -import { createAutoPipelineProxy } from "../pkg/auto-pipeline" // See https://github.com/upstash/upstash-redis/issues/342 // why we need this export diff --git a/pkg/types.ts b/pkg/types.ts index 0106c0e5..fddfc794 100644 --- a/pkg/types.ts +++ b/pkg/types.ts @@ -30,5 +30,5 @@ export type RedisOptions = { automaticDeserialization?: boolean; latencyLogging?: boolean; enableTelemetry?: boolean; - enableAutoPipelining?: boolean + enableAutoPipelining?: boolean; }; diff --git a/pkg/util.ts b/pkg/util.ts index 16d54077..5d273ec7 100644 --- a/pkg/util.ts +++ b/pkg/util.ts @@ -34,9 +34,9 @@ export function parseResponse(result: unknown): TResult { * Deserializes a scan result, excluding the cursor * which can be string "0" or a big number string. * Either way, we want it to stay as a string. - * - * @param result + * + * @param result */ export function deserializeScanResponse(result: [string, ...any]): TResult { return [result[0], ...parseResponse(result.slice(1))] as TResult; -} \ No newline at end of file +} From 734f2f56332bb1367f9fd413d6c11a996f9a0e44 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 12:27:45 +0300 Subject: [PATCH 5/9] fix: add regex explanation --- pkg/http.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/http.ts b/pkg/http.ts index 84b3fc7e..10df82f4 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -125,6 +125,16 @@ export class HttpClient implements Requester { }; this.baseUrl = config.baseUrl.replace(/\/$/, ""); + + /** + * regex to check if the baseUrl starts with http:// or https:// + * - `^` asserts the position at the start of the string. + * - `[^\s/$.?#]` makes sure that the domain starts correctly; + * without white space, '/', '$', '.', '?' or '#' + * - `.` matches any character except new line + * - `[^\s]*` matches anything except white space + * - `$` asserts the position at the end of the string. + */ const urlRegex = /^https?:\/\/[^\s/$.?#].[^\s]*$/; if (!urlRegex.test(this.baseUrl)) { throw new UrlError(this.baseUrl); From fc93463e6cf035328c2fd47616b41b935f8ecb79 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 12:29:52 +0300 Subject: [PATCH 6/9] fix: update bun.lockb --- bun.lockb | Bin 62409 -> 62409 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index af801cbd1eaf211b227944b6ce27b0f3a43a21e7..7727a90dec3dbec84c23812c9e6e54df65668204 100755 GIT binary patch delta 1044 zcmW;AJBZT&0EcmpgANW|ge*ePC5w`=ixMB_#n+jhuhi2sJ}L>4;v#`)Nj&%MTVU6ZdofwB`9n(p0g!XZr6vx}uM^`Ct&UED zBebvRq&UXbnobWV=v>vwaEk6Vog8QAt?LvxNB_D`iH$%VV1>;aIw20wxTzCijpi+# z7>8)x)=6-L_8pxR$Jn~7)58fm8#)7+Qu)}~GmC+KYHWH?3lsZNeF^q%PyI7k1vPKk{d>HsTj dzSIeEfJUqnVU1>2C+>FoKX)Ja^0)i_)jxhmKn(x@ delta 1044 zcmW;AJBZT&0Ecmp9vvLIna$wTMaY;jzT@LG=QH(n@g1KR&sQo489N9T@t|WT=_COa zvIs>Sa@=6ymbe96a$6OL;1u-#Ek8cK&F$9acI&OR=gs2A_l4c9-=7v=+TXtZT1!@b zEHC}Z+}H0vZ0-7zzWeBHI14-0jeWiUMwp|yUnj;Hb`R(zSfF)KXMiQzhjfNmp>tSg zgmZL{=!~&O&()b=gZ@#S6x+wt0cO}at`p)EjT1T%=4dYI#5lw5Nu2}>v`*;^uteL_ z8DfRbvd#$S=$_UYV~yS!oe4JRpVdjReNG)I|?%`;yKOD|9aFjBt+b6`e8G=v~#BV1xdOPKxb79bkr?YdRrL(YUS?VUFevofv1> zy{VI6fz~aZ0hVar))``j&K;c*&e08Z##p0wS7(9^`uB8FY~NQ0m|^FEPKZ-99_mDx zqxncD#u;{3brLMligX58qWxHBh!r|(IwPE;`$T7qHF{5VCfK0=Oee+mb9I0jc3$X& aI7K7Yi7-dAuM_us>pyqy{rtDT_39s0%s}-3 From bdd5a66b37e627048fcd478fa4090cb7529f8382 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 12:48:09 +0300 Subject: [PATCH 7/9] fix: update cloudflare-workers redis dependency --- examples/cloudflare-workers/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cloudflare-workers/package.json b/examples/cloudflare-workers/package.json index 7fec9ce3..e6848b23 100644 --- a/examples/cloudflare-workers/package.json +++ b/examples/cloudflare-workers/package.json @@ -12,6 +12,6 @@ "wrangler": "^2.20.0" }, "dependencies": { - "@upstash/redis": "link:../../dist" + "@upstash/redis": "latest" } } From 7bdd2b266aa6d3c769c92a3418616984d1f556b2 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 13:01:10 +0300 Subject: [PATCH 8/9] fix: bump node version in nextjs deployed tests tests were failing because they got node end-of-life errors at vercel --- .github/workflows/tests.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ab55f8c5..3d7869e7 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -134,7 +134,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - name: Setup Bun uses: oven-sh/setup-bun@v1 @@ -169,7 +169,7 @@ jobs: - name: Setup node uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 20 - name: Setup Bun uses: oven-sh/setup-bun@v1 From c222e638fdbce4a978a660fdc44816e95f78f095 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 26 Jun 2024 15:59:01 +0300 Subject: [PATCH 9/9] fix: update url message --- pkg/error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/error.ts b/pkg/error.ts index c6d90252..f57702f1 100644 --- a/pkg/error.ts +++ b/pkg/error.ts @@ -10,7 +10,7 @@ export class UpstashError extends Error { export class UrlError extends Error { constructor(url: string) { - super(`Upstash Redis client was passed an invalid URL: "${url}"`); + super(`Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". `); this.name = "UrlError"; } }