From 94a3edd77de0dc8056018cbb613fe79bfa7b7eaf Mon Sep 17 00:00:00 2001 From: Konsti Wohlwend Date: Sat, 3 Aug 2024 21:05:24 -0700 Subject: [PATCH] Svix concurrency workaround (#167) --- .github/workflows/e2e-api-tests.yaml | 35 +++++++++++++++---- .github/workflows/lint-and-build.yaml | 5 ++- apps/backend/src/lib/webhooks.tsx | 13 +++++-- apps/e2e/tests/general/lint.test.ts | 2 +- apps/e2e/tests/general/typecheck.test.ts | 2 +- .../cjs-test/app/handler/[...stack]/page.d.ts | 2 ++ .../handler/[...stack]/{page.tsx => page.jsx} | 4 +-- examples/cjs-test/app/page.d.ts | 2 ++ examples/cjs-test/app/{page.tsx => page.jsx} | 0 packages/stack-shared/src/utils/crypto.tsx | 13 ++++++- packages/stack-shared/src/utils/uuids.tsx | 4 +-- 11 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 examples/cjs-test/app/handler/[...stack]/page.d.ts rename examples/cjs-test/app/handler/[...stack]/{page.tsx => page.jsx} (65%) create mode 100644 examples/cjs-test/app/page.d.ts rename examples/cjs-test/app/{page.tsx => page.jsx} (100%) diff --git a/.github/workflows/e2e-api-tests.yaml b/.github/workflows/e2e-api-tests.yaml index ab1113336..a2c5019ac 100644 --- a/.github/workflows/e2e-api-tests.yaml +++ b/.github/workflows/e2e-api-tests.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: - node-version: [20.x] + node-version: [20.x, 22.x] steps: - uses: actions/checkout@v3 @@ -36,17 +36,35 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Create .env.test.local file for stack-backend + - name: Create .env.test.local file for apps/backend run: cp apps/backend/.env.development apps/backend/.env.test.local - - name: Create .env.test.local file for stack-dashboard + - name: Create .env.test.local file for apps/dashboard run: cp apps/dashboard/.env.development apps/dashboard/.env.test.local + + - name: Create .env.test.local file for apps/e2e + run: cp apps/e2e/.env.development apps/e2e/.env.test.local - - name: Build stack-backend - run: pnpm build:backend + - name: Create .env.test.local file for examples/cjs-test + run: cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local + + - name: Create .env.test.local file for examples/demo + run: cp examples/demo/.env.development examples/demo/.env.test.local - - name: Build stack-dashboard - run: pnpm build:dashboard + - name: Create .env.test.local file for examples/docs-examples + run: cp examples/docs-examples/.env.development examples/docs-examples/.env.test.local + + - name: Create .env.test.local file for examples/e-commerce + run: cp examples/e-commerce/.env.development examples/e-commerce/.env.test.local + + - name: Create .env.test.local file for examples/middleware + run: cp examples/middleware/.env.development examples/middleware/.env.test.local + + - name: Create .env.test.local file for examples/partial-prerendering + run: cp examples/partial-prerendering/.env.development examples/partial-prerendering/.env.test.local + + - name: Build + run: pnpm build - name: Start Docker Compose run: docker compose -f dependencies.compose.yaml up -d @@ -57,6 +75,9 @@ jobs: - name: Wait on Inbucket run: npx wait-on tcp:localhost:2500 + - name: Wait on Svix + run: npx wait-on tcp:localhost:8113 + - name: Initialize database run: pnpm run prisma -- migrate reset --force diff --git a/.github/workflows/lint-and-build.yaml b/.github/workflows/lint-and-build.yaml index 6a8d33d12..bbdc3251d 100644 --- a/.github/workflows/lint-and-build.yaml +++ b/.github/workflows/lint-and-build.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [18.x, 20.x] + node-version: [20.x, 22.x] steps: - uses: actions/checkout@v3 @@ -66,3 +66,6 @@ jobs: - name: Lint run: pnpm lint + + - name: Typecheck + run: pnpm typecheck diff --git a/apps/backend/src/lib/webhooks.tsx b/apps/backend/src/lib/webhooks.tsx index 0e2b2f7e7..61de29b0d 100644 --- a/apps/backend/src/lib/webhooks.tsx +++ b/apps/backend/src/lib/webhooks.tsx @@ -14,7 +14,16 @@ async function sendWebhooks(options: { const server = getEnvVariable("STACK_SVIX_SERVER_URL", "") || undefined; const svix = new Svix(apiKey, { serverUrl: server }); - await svix.application.getOrCreate({ uid: options.projectId, name: options.projectId }); + try { + await svix.application.getOrCreate({ uid: options.projectId, name: options.projectId }); + } catch (e: any) { + if (e.message.includes("409")) { + // This is a Svix bug; they are working on fixing it + // TODO next-release: remove this + console.warn("[no action required] Svix bug: 409 error when creating application. Remove this warning when Svix fixes this."); + } + console.warn("Error creating application in Svix", e); + } await svix.message.create(options.projectId, { eventType: options.type, payload: { @@ -41,4 +50,4 @@ export const sendUserUpdatedWebhook = createWebhookSender(userUpdatedWebhookEven export const sendUserDeletedWebhook = createWebhookSender(userDeletedWebhookEvent); export const sendTeamCreatedWebhook = createWebhookSender(teamCreatedWebhookEvent); export const sendTeamUpdatedWebhook = createWebhookSender(teamUpdatedWebhookEvent); -export const sendTeamDeletedWebhook = createWebhookSender(teamDeletedWebhookEvent); \ No newline at end of file +export const sendTeamDeletedWebhook = createWebhookSender(teamDeletedWebhookEvent); diff --git a/apps/e2e/tests/general/lint.test.ts b/apps/e2e/tests/general/lint.test.ts index a7641a889..ba79eb34f 100644 --- a/apps/e2e/tests/general/lint.test.ts +++ b/apps/e2e/tests/general/lint.test.ts @@ -10,5 +10,5 @@ describe.todo("`pnpm run lint`", () => { }); }); expect(error, `Expected no error to be thrown!\n\n\n\nstdout: ${stdout}\n\n\n\nstderr: ${stderr}`).toBeNull(); - }, 60_000); + }, 120_000); }); diff --git a/apps/e2e/tests/general/typecheck.test.ts b/apps/e2e/tests/general/typecheck.test.ts index 73729a789..f77a68830 100644 --- a/apps/e2e/tests/general/typecheck.test.ts +++ b/apps/e2e/tests/general/typecheck.test.ts @@ -10,5 +10,5 @@ describe.todo("`pnpm run typecheck`", () => { }); }); expect(error, `Expected no error to be thrown!\n\n\n\nstdout: ${stdout}\n\n\n\nstderr: ${stderr}`).toBeNull(); - }, 60_000); + }, 120_000); }); diff --git a/examples/cjs-test/app/handler/[...stack]/page.d.ts b/examples/cjs-test/app/handler/[...stack]/page.d.ts new file mode 100644 index 000000000..1c6fc62a2 --- /dev/null +++ b/examples/cjs-test/app/handler/[...stack]/page.d.ts @@ -0,0 +1,2 @@ +declare const Handler: (props: any) => JSX.Element; +export default Handler; diff --git a/examples/cjs-test/app/handler/[...stack]/page.tsx b/examples/cjs-test/app/handler/[...stack]/page.jsx similarity index 65% rename from examples/cjs-test/app/handler/[...stack]/page.tsx rename to examples/cjs-test/app/handler/[...stack]/page.jsx index 1b248eb98..2c16bf2de 100644 --- a/examples/cjs-test/app/handler/[...stack]/page.tsx +++ b/examples/cjs-test/app/handler/[...stack]/page.jsx @@ -1,7 +1,7 @@ -import { StackHandler } from "@stackframe/stack"; +const { StackHandler } = require("@stackframe/stack"); const { stackServerApp } = require("../../../stack"); -function Handler(props: any) { +function Handler(props) { return ; } diff --git a/examples/cjs-test/app/page.d.ts b/examples/cjs-test/app/page.d.ts new file mode 100644 index 000000000..76afbb6a2 --- /dev/null +++ b/examples/cjs-test/app/page.d.ts @@ -0,0 +1,2 @@ +declare const Home: (props: any) => JSX.Element; +export default Home; diff --git a/examples/cjs-test/app/page.tsx b/examples/cjs-test/app/page.jsx similarity index 100% rename from examples/cjs-test/app/page.tsx rename to examples/cjs-test/app/page.jsx diff --git a/packages/stack-shared/src/utils/crypto.tsx b/packages/stack-shared/src/utils/crypto.tsx index 902b53e0b..150323407 100644 --- a/packages/stack-shared/src/utils/crypto.tsx +++ b/packages/stack-shared/src/utils/crypto.tsx @@ -1,6 +1,17 @@ import { encodeBase32 } from "./bytes"; +import { StackAssertionError } from "./errors"; import { globalVar } from "./globals"; +export function generateRandomValues(array: Uint8Array) { + if (!globalVar.crypto) { + throw new StackAssertionError("Crypto API is not available in this environment. Are you using an old browser?"); + } + if (!globalVar.crypto.getRandomValues) { + throw new StackAssertionError("crypto.getRandomValues is not available in this environment. Are you using an old browser?"); + } + return globalVar.crypto.getRandomValues(array); +} + /** * Generates a secure alphanumeric string using the system's cryptographically secure * random number generator. @@ -8,7 +19,7 @@ import { globalVar } from "./globals"; export function generateSecureRandomString(minBitsOfEntropy: number = 224) { const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5); const bytesCount = Math.ceil(base32CharactersCount * 5 / 8); - const randomBytes = globalVar.crypto.getRandomValues(new Uint8Array(bytesCount)); + const randomBytes = generateRandomValues(new Uint8Array(bytesCount)); const str = encodeBase32(randomBytes); return str.slice(str.length - base32CharactersCount).toLowerCase(); } diff --git a/packages/stack-shared/src/utils/uuids.tsx b/packages/stack-shared/src/utils/uuids.tsx index 3e8cb1c57..ca4e84004 100644 --- a/packages/stack-shared/src/utils/uuids.tsx +++ b/packages/stack-shared/src/utils/uuids.tsx @@ -1,9 +1,9 @@ -import { globalVar } from "./globals"; +import { generateRandomValues } from "./crypto"; export function generateUuid() { // crypto.randomUuid is not supported in all browsers, so this is a polyfill return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => - (+c ^ globalVar.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16) + (+c ^ generateRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16) ); }