diff --git a/package.json b/package.json index 02f20fa3..c28d7884 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,7 @@ "@types/ws": "^8", "prettier": "^2.7.1", "replayio": "workspace:^", - "typescript": "^5.4.5", - "ws": "^8.17.0" + "typescript": "^5.4.5" }, "dependencies": { "node-fetch": "^3.3.1" diff --git a/packages/playwright/jest.config.js b/packages/playwright/jest.config.js new file mode 100644 index 00000000..2abe8413 --- /dev/null +++ b/packages/playwright/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + testTimeout: 20000, + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/(*.)+(spec|test).[jt]s?(x)", "!**/fixtures-app/**"], + // TODO: implement dependencyExtractor: the whole src directory and the whole fixture app should be added here +}; diff --git a/packages/playwright/package.json b/packages/playwright/package.json index fe0abcb1..261cebca 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -17,8 +17,9 @@ "scripts": { "prepare": "yarn run build", "build": "rm -rf dist/ tsconfig.tsbuildinfo && tsc", - "test": "echo \"Error: no test specified\"", - "typecheck": "tsc --noEmit" + "test": "jest", + "typecheck": "tsc --noEmit", + "fixtures-app": "next dev tests/fixtures-app" }, "devDependencies": { "@playwright/test": "^1.40.1", @@ -28,6 +29,12 @@ "@types/stack-utils": "^2.0.3", "@types/uuid": "^8.3.4", "@types/ws": "^8.5.10", + "jest": "^28.1.3", + "msw": "2.3.0-ws.rc-6", + "next": "^14.2.4", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "ts-jest": "^28.0.6", "typescript": "^5.4.5" }, "repository": { @@ -45,6 +52,7 @@ "@replayio/test-utils": "workspace:^", "debug": "^4.3.4", "stack-utils": "^2.0.6", + "undici": "^5.28.4", "uuid": "^8.3.2", "ws": "^8.13.0" }, diff --git a/packages/playwright/tests/fixtures-app/.gitignore b/packages/playwright/tests/fixtures-app/.gitignore new file mode 100644 index 00000000..fd3dbb57 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/playwright/tests/fixtures-app/app/_pw-utils/network-mock-global-setup.ts b/packages/playwright/tests/fixtures-app/app/_pw-utils/network-mock-global-setup.ts new file mode 100644 index 00000000..e21a277d --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/_pw-utils/network-mock-global-setup.ts @@ -0,0 +1,121 @@ +import type { FullConfig } from "@playwright/test"; +import type { + addOriginalSourceResult, + addSourceMapResult, + beginRecordingUploadParameters, + beginRecordingUploadResult, + endRecordingUploadResult, + existsResult, + setAccessTokenResult, + setRecordingMetadataResult, +} from "@replayio/protocol"; +import { WebSocket } from "undici"; + +async function globalSetup(_config: FullConfig) { + (globalThis as any).WebSocket = WebSocket; + + const { http, ws, HttpResponse } = await import("msw"); + const { setupServer } = await import("msw/node"); + + const server = setupServer( + http.get("*", async ({ request }) => { + switch (request.url) { + case "https://dispatch.replay.io/": { + return new HttpResponse("", { + status: 200, + }); + } + default: + throw new Error(`Unexpected GET to: ${request.url}`); + } + }), + http.post("*", async ({ request }) => { + switch (request.url) { + case "https://api.replay.io/v1/graphql": { + const body = JSON.parse(await request.text()); + + switch (body.name) { + case "AddTestsToShard": + // TODO: we are interested in the data that we sent out here + return new HttpResponse(JSON.stringify({})); + case "CompleteTestRunShard": + return new HttpResponse(JSON.stringify({})); + case "CreateTestRunShard": + return new HttpResponse( + JSON.stringify({ + data: { + startTestRunShard: { + testRunShardId: "test-run-shard-id", + }, + }, + }) + ); + default: + throw new Error(`Unexpected graphql operation name: ${body.name}`); + } + } + case "https://webhooks.replay.io/api/metrics": + return new HttpResponse(JSON.stringify({})); + default: + throw new Error(`Unexpected POST to: ${request.url}`); + } + }), + http.put("*", async ({ request }) => { + if (request.url.startsWith("https://app.replay.io/recording/")) { + return new HttpResponse(JSON.stringify({})); + } + throw new Error(`Unexpected PUT to: ${request.url}`); + }) + ); + + const wsHandler = ws.link("wss://dispatch.replay.io").on("connection", ({ client, server }) => { + server.connect(); + + client.addEventListener("message", event => { + event.preventDefault(); + const data = JSON.parse(String(event.data)); + switch (data.method) { + case "Authentication.setAccessToken": + case "Internal.endRecordingUpload": + case "Internal.setRecordingMetadata": + case "Recording.addOriginalSource": + case "Recording.addSourceMap": + case "Resource.exists": + client.send( + JSON.stringify({ + id: data.id, + result: {} satisfies + | addOriginalSourceResult + | addSourceMapResult + | endRecordingUploadResult + | existsResult + | setAccessTokenResult + | setRecordingMetadataResult, + }) + ); + return; + case "Internal.beginRecordingUpload": { + const params: beginRecordingUploadParameters = data.params; + client.send( + JSON.stringify({ + id: data.id, + result: { + recordingId: params.recordingId!, + uploadLink: `https://app.replay.io/recording/${params.recordingId}`, + } satisfies beginRecordingUploadResult, + }) + ); + return; + } + default: + throw new Error(`Unexpected protocol method: ${data.method}`); + } + }); + }); + + server.use(wsHandler); + + server.listen(); +} + +export default globalSetup; diff --git a/packages/playwright/tests/fixtures-app/app/basic/page.module.css b/packages/playwright/tests/fixtures-app/app/basic/page.module.css new file mode 100644 index 00000000..5c4b1e6a --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/basic/page.module.css @@ -0,0 +1,230 @@ +.main { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 6rem; + min-height: 100vh; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.description a { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; +} + +.description p { + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.grid { + display: grid; + grid-template-columns: repeat(4, minmax(25%, auto)); + max-width: 100%; + width: var(--max-width); +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; + text-wrap: balance; +} + +.center { + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; +} + +.center::before { + background: var(--secondary-glow); + border-radius: 50%; + width: 480px; + height: 360px; + margin-left: -400px; +} + +.center::after { + background: var(--primary-glow); + width: 240px; + height: 180px; + z-index: -1; +} + +.center::before, +.center::after { + content: ""; + left: 50%; + position: absolute; + filter: blur(45px); + transform: translateZ(0); +} + +.logo { + position: relative; +} +/* Enable hover only on non-touch devices */ +@media (hover: hover) and (pointer: fine) { + .card:hover { + background: rgba(var(--card-rgb), 0.1); + border: 1px solid rgba(var(--card-border-rgb), 0.15); + } + + .card:hover span { + transform: translateX(4px); + } +} + +@media (prefers-reduced-motion) { + .card:hover span { + transform: none; + } +} + +/* Mobile */ +@media (max-width: 700px) { + .content { + padding: 4rem; + } + + .grid { + grid-template-columns: 1fr; + margin-bottom: 120px; + max-width: 320px; + text-align: center; + } + + .card { + padding: 1rem 2.5rem; + } + + .card h2 { + margin-bottom: 0.5rem; + } + + .center { + padding: 8rem 0 6rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + .grid { + grid-template-columns: repeat(2, 50%); + } +} + +@media (prefers-color-scheme: dark) { + .vercelLogo { + filter: invert(1); + } + + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } +} + +@keyframes rotate { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/packages/playwright/tests/fixtures-app/app/basic/page.tsx b/packages/playwright/tests/fixtures-app/app/basic/page.tsx new file mode 100644 index 00000000..0d945a2e --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/basic/page.tsx @@ -0,0 +1,94 @@ +import Image from "next/image"; +import styles from "./page.module.css"; + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+
+ + By{" "} + Vercel Logo + +
+
+ +
+ Next.js Logo +
+ +
+ +

+ Docs -> +

+

Find in-depth information about Next.js features and API.

+
+ + +

+ Learn -> +

+

Learn about Next.js in an interactive course with quizzes!

+
+ + +

+ Templates -> +

+

Explore starter templates for Next.js.

+
+ + +

+ Deploy -> +

+

Instantly deploy your Next.js site to a shareable URL with Vercel.

+
+
+
+ ); +} diff --git a/packages/playwright/tests/fixtures-app/app/basic/playwright.config.mts b/packages/playwright/tests/fixtures-app/app/basic/playwright.config.mts new file mode 100644 index 00000000..147d61dc --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/basic/playwright.config.mts @@ -0,0 +1,19 @@ +import { defineConfig } from "@playwright/test"; +import { devices as replayDevices, replayReporter } from "@replayio/playwright"; + +export default defineConfig({ + globalSetup: "../_pw-utils/network-mock-global-setup.ts", + reporter: [ + ["line"], + replayReporter({ + upload: true, + apiKey: "MOCKED_API_KEY", + }), + ], + projects: [ + { + name: "replay-chromium", + use: { ...replayDevices["Replay Chromium"] }, + }, + ], +}); diff --git a/packages/playwright/tests/fixtures-app/app/basic/tests/pw.spec.ts b/packages/playwright/tests/fixtures-app/app/basic/tests/pw.spec.ts new file mode 100644 index 00000000..5fba6af7 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/basic/tests/pw.spec.ts @@ -0,0 +1,9 @@ +import { test, expect } from "@playwright/test"; + +const testName = __dirname.split("/").slice(-2)[0]; + +test("smoke", async ({ page }) => { + await page.goto(`http://localhost:3000/${testName}`); + const title = page.locator("#docs-card p"); + await expect(title).toHaveText("Find in-depth information about Next.js features and API."); +}); diff --git a/packages/playwright/tests/fixtures-app/app/favicon.ico b/packages/playwright/tests/fixtures-app/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/packages/playwright/tests/fixtures-app/app/favicon.ico differ diff --git a/packages/playwright/tests/fixtures-app/app/globals.css b/packages/playwright/tests/fixtures-app/app/globals.css new file mode 100644 index 00000000..f4bd77c0 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/globals.css @@ -0,0 +1,107 @@ +:root { + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", + "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", + "Fira Mono", "Droid Sans Mono", "Courier New", monospace; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; + + --primary-glow: conic-gradient( + from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg + ); + --secondary-glow: radial-gradient( + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); + + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} diff --git a/packages/playwright/tests/fixtures-app/app/layout.tsx b/packages/playwright/tests/fixtures-app/app/layout.tsx new file mode 100644 index 00000000..3314e478 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/packages/playwright/tests/fixtures-app/app/page.module.css b/packages/playwright/tests/fixtures-app/app/page.module.css new file mode 100644 index 00000000..5c4b1e6a --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/page.module.css @@ -0,0 +1,230 @@ +.main { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 6rem; + min-height: 100vh; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.description a { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; +} + +.description p { + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.grid { + display: grid; + grid-template-columns: repeat(4, minmax(25%, auto)); + max-width: 100%; + width: var(--max-width); +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; + text-wrap: balance; +} + +.center { + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; +} + +.center::before { + background: var(--secondary-glow); + border-radius: 50%; + width: 480px; + height: 360px; + margin-left: -400px; +} + +.center::after { + background: var(--primary-glow); + width: 240px; + height: 180px; + z-index: -1; +} + +.center::before, +.center::after { + content: ""; + left: 50%; + position: absolute; + filter: blur(45px); + transform: translateZ(0); +} + +.logo { + position: relative; +} +/* Enable hover only on non-touch devices */ +@media (hover: hover) and (pointer: fine) { + .card:hover { + background: rgba(var(--card-rgb), 0.1); + border: 1px solid rgba(var(--card-border-rgb), 0.15); + } + + .card:hover span { + transform: translateX(4px); + } +} + +@media (prefers-reduced-motion) { + .card:hover span { + transform: none; + } +} + +/* Mobile */ +@media (max-width: 700px) { + .content { + padding: 4rem; + } + + .grid { + grid-template-columns: 1fr; + margin-bottom: 120px; + max-width: 320px; + text-align: center; + } + + .card { + padding: 1rem 2.5rem; + } + + .card h2 { + margin-bottom: 0.5rem; + } + + .center { + padding: 8rem 0 6rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + .grid { + grid-template-columns: repeat(2, 50%); + } +} + +@media (prefers-color-scheme: dark) { + .vercelLogo { + filter: invert(1); + } + + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } +} + +@keyframes rotate { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/packages/playwright/tests/fixtures-app/app/page.tsx b/packages/playwright/tests/fixtures-app/app/page.tsx new file mode 100644 index 00000000..81070906 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/app/page.tsx @@ -0,0 +1,95 @@ +import Image from "next/image"; +import styles from "./page.module.css"; + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+
+ + By{" "} + Vercel Logo + +
+
+ +
+ Next.js Logo +
+ +
+ +

+ Docs -> +

+

Find in-depth information about Next.js features and API.

+
+ + +

+ Learn -> +

+

Learn about Next.js in an interactive course with quizzes!

+
+ + +

+ Templates -> +

+

Explore starter templates for Next.js.

+
+ + +

+ Deploy -> +

+

+ Instantly deploy your Next.js site to a shareable URL with Vercel. +

+
+
+
+ ); +} diff --git a/packages/playwright/tests/fixtures-app/next.config.mjs b/packages/playwright/tests/fixtures-app/next.config.mjs new file mode 100644 index 00000000..4678774e --- /dev/null +++ b/packages/playwright/tests/fixtures-app/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/packages/playwright/tests/fixtures-app/public/next.svg b/packages/playwright/tests/fixtures-app/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/packages/playwright/tests/fixtures-app/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/playwright/tests/fixtures-app/public/vercel.svg b/packages/playwright/tests/fixtures-app/public/vercel.svg new file mode 100644 index 00000000..d2f84222 --- /dev/null +++ b/packages/playwright/tests/fixtures-app/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/playwright/tests/fixtures-app/tsconfig.json b/packages/playwright/tests/fixtures-app/tsconfig.json new file mode 100644 index 00000000..bb5584ed --- /dev/null +++ b/packages/playwright/tests/fixtures-app/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/playwright/tests/index.test.ts b/packages/playwright/tests/index.test.ts new file mode 100644 index 00000000..71ddcfb4 --- /dev/null +++ b/packages/playwright/tests/index.test.ts @@ -0,0 +1,38 @@ +import cp from "child_process"; +import path from "path"; +import fs from "fs"; + +const fixturesPages = path.join(__dirname, "fixtures-app", "app"); +const playwrightPath = cp.spawnSync("yarn", ["bin", "playwright"]).stdout.toString().trim(); + +fs.readdirSync(fixturesPages).forEach(name => { + if (name.startsWith("_") || !fs.statSync(path.join(fixturesPages, name)).isDirectory()) { + return; + } + it(name, async () => { + await new Promise((resolve, reject) => { + const child = cp.spawn("node", [playwrightPath, "test", "--project", "replay-chromium"], { + cwd: path.join(fixturesPages, name), + stdio: "inherit", + env: { + ...process.env, + // so Playwirhgt doesn't think the test was run by Jest. If it sees this env variable it throws an error + JEST_WORKER_ID: undefined, + }, + }); + + child.on("error", reject); + + child.on("exit", (code, signal) => { + if (code || signal) { + if (signal === "SIGTERM") { + return; + } + reject(new Error(`Process failed (${code ? `code: ${code}` : `signal: ${signal}`})`)); + } else { + resolve(); + } + }); + }); + }); +}); diff --git a/packages/replay/package.json b/packages/replay/package.json index e13ac1a3..ba8a7dbf 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -48,7 +48,7 @@ "semver": "^7.5.4", "superstruct": "^0.15.4", "text-table": "^0.2.0", - "ws": "^7.5.0" + "undici": "^5.28.4" }, "devDependencies": { "@replay-cli/tsconfig": "workspace:^", diff --git a/packages/replay/src/client.ts b/packages/replay/src/client.ts index 337107d3..b80f2510 100644 --- a/packages/replay/src/client.ts +++ b/packages/replay/src/client.ts @@ -1,7 +1,6 @@ import dbg from "./debug"; -import WebSocket from "ws"; +import { WebSocket } from "undici"; import { defer } from "./utils"; -import { Agent } from "http"; const debug = dbg("replay:protocol"); @@ -45,17 +44,15 @@ class ProtocolClient { eventListeners = new Map(); nextMessageId = 1; - constructor(address: string, callbacks: Callbacks, agent?: Agent) { - debug("Creating WebSocket for %s with %o", address, { agent }); - this.socket = new WebSocket(address, { - agent: agent, - }); + constructor(address: string, callbacks: Callbacks) { + debug("Creating WebSocket for %s", address); + this.socket = new ((globalThis as any).WebSocket || WebSocket)(address); this.callbacks = callbacks; - this.socket.on("open", callbacks.onOpen); - this.socket.on("close", callbacks.onClose); - this.socket.on("error", callbacks.onError); - this.socket.on("message", message => this.onMessage(message)); + this.socket.addEventListener("open", () => callbacks.onOpen(this.socket)); + this.socket.addEventListener("close", () => callbacks.onClose(this.socket)); + this.socket.addEventListener("error", () => callbacks.onError(this.socket)); + this.socket.addEventListener("message", message => this.onMessage(message.data)); } close() { @@ -79,31 +76,24 @@ class ProtocolClient { async sendCommand>( method: string, params: P, - data?: any, - sessionId?: string, - callback?: (err?: Error) => void + sessionId?: string ) { const id = this.nextMessageId++; debug("Sending command %s: %o", method, { id, params, sessionId }); - this.socket.send( - JSON.stringify({ - id, - method, - params, - binary: data ? true : undefined, - sessionId, - }), - err => { - if (!err && data) { - this.socket.send(data, callback); - } else { - if (err) { - debug("Received socket error: %s", err); - } - callback?.(err); - } - } - ); + try { + this.socket.send( + JSON.stringify({ + id, + method, + params, + sessionId, + }) + ); + } catch (err) { + debug("Received socket error: %s", err); + throw err; + } + const waiter = defer(); this.pendingMessages.set(id, waiter); return waiter.promise; @@ -113,7 +103,7 @@ class ProtocolClient { this.eventListeners.set(method, callback); } - onMessage(contents: WebSocket.RawData) { + onMessage(contents: any) { const msg = JSON.parse(String(contents)); debug("Received message %o", msg); if (msg.id) { diff --git a/packages/replay/src/main.ts b/packages/replay/src/main.ts index aea1a9ba..0540034f 100644 --- a/packages/replay/src/main.ts +++ b/packages/replay/src/main.ts @@ -4,7 +4,7 @@ import { getPackument } from "query-registry"; import { compare } from "semver"; import dbg from "./debug"; import { query } from "./graphql"; -import { getCurrentVersion, getHttpAgent } from "./utils"; +import { getCurrentVersion } from "./utils"; // requiring v4 explicitly because it's the last version with commonjs support. // Should be upgraded to the latest when converting this code to es modules. @@ -145,12 +145,11 @@ async function doUploadCrash( server: string, recording: RecordingEntry, verbose?: boolean, - apiKey?: string, - agent?: Agent + apiKey?: string ) { const client = new ReplayClient(); maybeLog(verbose, `Starting crash data upload for ${recording.id}...`); - if (!(await client.initConnection(server, apiKey, verbose, agent))) { + if (!(await client.initConnection(server, apiKey, verbose))) { maybeLog(verbose, `Crash data upload failed: can't connect to server ${server}`); return null; } @@ -327,11 +326,9 @@ async function doUploadRecording( apiKey = await readToken({ directory: dir }); } - const agent = getHttpAgent(server, agentOptions); - if (recording.status == "crashed") { debug("Uploading crash %o", recording); - await doUploadCrash(dir, server, recording, verbose, apiKey, agent); + await doUploadCrash(dir, server, recording, verbose, apiKey); maybeLog(verbose, `Crash report uploaded for ${recording.id}`); if (removeAssets) { removeRecordingAssets(recording, { directory: dir }); @@ -343,7 +340,7 @@ async function doUploadRecording( debug("Uploading recording %o", recording); const client = new ReplayClient(); - if (!(await client.initConnection(server, apiKey, verbose, agent))) { + if (!(await client.initConnection(server, apiKey, verbose))) { handleUploadingError(`Cannot connect to server ${server}`, strict, verbose); return null; } @@ -468,7 +465,6 @@ async function uploadRecording(id: string, opts: UploadOptions = {}) { async function processUploadedRecording(recordingId: string, opts: Options) { const server = getServer(opts); - const agent = getHttpAgent(server, opts.agentOptions); const { verbose } = opts; let apiKey = opts.apiKey; @@ -479,7 +475,7 @@ async function processUploadedRecording(recordingId: string, opts: Options) { } const client = new ReplayClient(); - if (!(await client.initConnection(server, apiKey, verbose, agent))) { + if (!(await client.initConnection(server, apiKey, verbose))) { maybeLog(verbose, `Processing failed: can't connect to server ${server}`); return false; } diff --git a/packages/replay/src/upload.ts b/packages/replay/src/upload.ts index 5b02f59f..bf4ce35c 100644 --- a/packages/replay/src/upload.ts +++ b/packages/replay/src/upload.ts @@ -21,31 +21,27 @@ class ReplayClient { client: ProtocolClient | undefined; clientReady = defer(); - async initConnection(server: string, accessToken?: string, verbose?: boolean, agent?: Agent) { + async initConnection(server: string, accessToken?: string, verbose?: boolean) { if (!this.client) { let { resolve } = this.clientReady; - this.client = new ProtocolClient( - server, - { - onOpen: async () => { - try { - await this.client!.setAccessToken(accessToken); - resolve(true); - } catch (err) { - maybeLog(verbose, `Error authenticating with server: ${err}`); - resolve(false); - } - }, - onClose() { - resolve(false); - }, - onError(e) { - maybeLog(verbose, `Error connecting to server: ${e}`); + this.client = new ProtocolClient(server, { + onOpen: async () => { + try { + await this.client!.setAccessToken(accessToken); + resolve(true); + } catch (err) { + maybeLog(verbose, `Error authenticating with server: ${err}`); resolve(false); - }, + } }, - agent - ); + onClose() { + resolve(false); + }, + onError(e) { + maybeLog(verbose, `Error connecting to server: ${e}`); + resolve(false); + }, + }); } return this.clientReady.promise; @@ -151,7 +147,7 @@ class ReplayClient { this.client.setEventListener("Session.unprocessedRegions", () => {}); this.client - .sendCommand("Session.ensureProcessed", { level: "basic" }, null, sessionId) + .sendCommand("Session.ensureProcessed", { level: "basic" }, sessionId) .then(() => waiter.resolve(null)); const error = await waiter.promise; diff --git a/packages/replayio/package.json b/packages/replayio/package.json index b55e21d6..88b2784a 100644 --- a/packages/replayio/package.json +++ b/packages/replayio/package.json @@ -55,8 +55,7 @@ "strip-ansi": "^6.0.1", "superstruct": "^0.15.4", "table": "^6.8.2", - "undici": "^5.28.4", - "ws": "^7.5.0" + "undici": "^5.28.4" }, "devDependencies": { "@replay-cli/tsconfig": "workspace:^", diff --git a/packages/replayio/src/utils/protocol/ProtocolClient.ts b/packages/replayio/src/utils/protocol/ProtocolClient.ts index 64f00053..aea2c7fc 100644 --- a/packages/replayio/src/utils/protocol/ProtocolClient.ts +++ b/packages/replayio/src/utils/protocol/ProtocolClient.ts @@ -1,6 +1,6 @@ import { SessionId, sessionError } from "@replayio/protocol"; import assert from "assert"; -import WebSocket from "ws"; +import { WebSocket } from "undici"; import { replayWsServer } from "../../config"; import { getAccessToken } from "../authentication/getAccessToken"; import { Deferred, STATUS_PENDING, createDeferred } from "../async/createDeferred"; @@ -28,10 +28,10 @@ export default class ProtocolClient { this.socket = new WebSocket(replayWsServer); - this.socket.on("close", this.onSocketClose); - this.socket.on("error", this.onSocketError); - this.socket.on("open", this.onSocketOpen); - this.socket.on("message", this.onSocketMessage); + this.socket.addEventListener("close", this.onSocketClose); + this.socket.addEventListener("error", this.onSocketError); + this.socket.addEventListener("open", this.onSocketOpen); + this.socket.addEventListener("message", event => this.onSocketMessage(event.data)); this.listenForMessage("Recording.sessionError", (error: sessionError) => { if (error.sessionId) { @@ -91,11 +91,12 @@ export default class ProtocolClient { sessionId, }; - this.socket.send(JSON.stringify(command), error => { - if (error) { - debug("Received socket error: %s", error); - } - }); + try { + this.socket.send(JSON.stringify(command)); + } catch (error) { + debug("Socket error when sending: %s", error); + throw error; + } const deferred = createDeferred({ sessionId, command }); @@ -122,7 +123,7 @@ export default class ProtocolClient { } }; - private onSocketMessage = (contents: WebSocket.RawData) => { + private onSocketMessage = (contents: any) => { const { error, id, method, params, result } = JSON.parse(String(contents)); if (id) { diff --git a/packages/replayio/src/utils/protocol/types.ts b/packages/replayio/src/utils/protocol/types.ts deleted file mode 100644 index 173f8f15..00000000 --- a/packages/replayio/src/utils/protocol/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { WebSocket } from "ws"; - -export type Callbacks = { - onOpen: (socket: WebSocket) => void; - onClose: (socket: WebSocket) => void; - onError: (socket: WebSocket) => void; -}; diff --git a/yarn.lock b/yarn.lock index a3e635ea..ff8fe5c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1789,6 +1789,24 @@ __metadata: languageName: node linkType: hard +"@bundled-es-modules/cookie@npm:^2.0.0": + version: 2.0.0 + resolution: "@bundled-es-modules/cookie@npm:2.0.0" + dependencies: + cookie: "npm:^0.5.0" + checksum: 10c0/0655dd331b35d7b5b6dd2301c3bcfb7233018c0e3235a40ced1d53f00463ab92dc01f0091f153812867bc0ef0f8e0a157a30acb16e8d7ef149702bf8db9fe7a6 + languageName: node + linkType: hard + +"@bundled-es-modules/statuses@npm:^1.0.1": + version: 1.0.1 + resolution: "@bundled-es-modules/statuses@npm:1.0.1" + dependencies: + statuses: "npm:^2.0.1" + checksum: 10c0/c1a8ede3efa8da61ccda4b98e773582a9733edfbeeee569d4630785f8e018766202edb190a754a3ec7a7f6bd738e857829affc2fdb676b6dab4db1bb44e62785 + languageName: node + linkType: hard + "@changesets/apply-release-plan@npm:^7.0.3": version: 7.0.3 resolution: "@changesets/apply-release-plan@npm:7.0.3" @@ -2402,6 +2420,51 @@ __metadata: languageName: node linkType: hard +"@inquirer/confirm@npm:^3.0.0": + version: 3.1.10 + resolution: "@inquirer/confirm@npm:3.1.10" + dependencies: + "@inquirer/core": "npm:^8.2.3" + "@inquirer/type": "npm:^1.3.3" + checksum: 10c0/a69bc0d21807b8cecb6ab1a9e0653abd255d1dde34083f536ee59df1bf01b1614fd6824b25735d2ddd8e5d81192a67e1da8944dc1713dbaeb75c7c182a4d6b88 + languageName: node + linkType: hard + +"@inquirer/core@npm:^8.2.3": + version: 8.2.3 + resolution: "@inquirer/core@npm:8.2.3" + dependencies: + "@inquirer/figures": "npm:^1.0.3" + "@inquirer/type": "npm:^1.3.3" + "@types/mute-stream": "npm:^0.0.4" + "@types/node": "npm:^20.14.6" + "@types/wrap-ansi": "npm:^3.0.0" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^4.1.2" + cli-spinners: "npm:^2.9.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^1.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + checksum: 10c0/5e618df6bac115e29ba2da01f5b142da53f2502679ebebccc9aa185e04261cae279c856e07bdecd69d3ad9eeec87729b7c212c59242374b982bdaa45ceb69ae4 + languageName: node + linkType: hard + +"@inquirer/figures@npm:^1.0.3": + version: 1.0.3 + resolution: "@inquirer/figures@npm:1.0.3" + checksum: 10c0/099e062f000baafb4010014ece443d0cd211f562194854dc52a128bfe514611f8cc3da4cdb5092d75440956aff201dcd8e893b8a71feb104f97b0b00c6a696cf + languageName: node + linkType: hard + +"@inquirer/type@npm:^1.3.3": + version: 1.3.3 + resolution: "@inquirer/type@npm:1.3.3" + checksum: 10c0/84048694410bb5b2c55dc4e48da6369ce942b9e8f8016d15ef63ab3f9873832a56cc12a50582b5b2b626821bfe29973dfcc19edc90f58490b51df1f4d9d0a074 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2939,6 +3002,27 @@ __metadata: languageName: node linkType: hard +"@mswjs/cookies@npm:^1.1.0": + version: 1.1.1 + resolution: "@mswjs/cookies@npm:1.1.1" + checksum: 10c0/75d3fdb0b5941ff473776b25869e4e7d1aeae872107c8cd12f1ebd89e1a9fb39473c9fbd1278aec91f40c991cd9fe98533863ef3a3906ca060532ce49acf86a5 + languageName: node + linkType: hard + +"@mswjs/interceptors@npm:^0.27.1": + version: 0.27.2 + resolution: "@mswjs/interceptors@npm:0.27.2" + dependencies: + "@open-draft/deferred-promise": "npm:^2.2.0" + "@open-draft/logger": "npm:^0.3.0" + "@open-draft/until": "npm:^2.0.0" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.2.1" + strict-event-emitter: "npm:^0.5.1" + checksum: 10c0/7498cf89059eed1f42433bbee31ab6a88955bd377379f2f9cb34623eb00c48002975abff9bdf90489ce5832684f09b3b61a8801b161ec3f86c8015ee4e5b18f5 + languageName: node + linkType: hard + "@napi-rs/snappy-android-arm-eabi@npm:7.2.2": version: 7.2.2 resolution: "@napi-rs/snappy-android-arm-eabi@npm:7.2.2" @@ -3030,6 +3114,76 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:14.2.4": + version: 14.2.4 + resolution: "@next/env@npm:14.2.4" + checksum: 10c0/cc284e3dd0666df04d8321645d8409c10cb8e325884c226abbb2e7bea20f0a4232f988216aa506a9d0457b46f28b594a61179d1e978c0ca22497cd8cab8196c7 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-darwin-arm64@npm:14.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-darwin-x64@npm:14.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-linux-x64-musl@npm:14.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-ia32-msvc@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:14.2.4": + version: 14.2.4 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1": version: 5.1.1-v1 resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1" @@ -3088,6 +3242,30 @@ __metadata: languageName: node linkType: hard +"@open-draft/deferred-promise@npm:^2.2.0": + version: 2.2.0 + resolution: "@open-draft/deferred-promise@npm:2.2.0" + checksum: 10c0/eafc1b1d0fc8edb5e1c753c5e0f3293410b40dde2f92688211a54806d4136887051f39b98c1950370be258483deac9dfd17cf8b96557553765198ef2547e4549 + languageName: node + linkType: hard + +"@open-draft/logger@npm:^0.3.0": + version: 0.3.0 + resolution: "@open-draft/logger@npm:0.3.0" + dependencies: + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.0" + checksum: 10c0/90010647b22e9693c16258f4f9adb034824d1771d3baa313057b9a37797f571181005bc50415a934eaf7c891d90ff71dcd7a9d5048b0b6bb438f31bef2c7c5c1 + languageName: node + linkType: hard + +"@open-draft/until@npm:^2.0.0, @open-draft/until@npm:^2.1.0": + version: 2.1.0 + resolution: "@open-draft/until@npm:2.1.0" + checksum: 10c0/61d3f99718dd86bb393fee2d7a785f961dcaf12f2055f0c693b27f4d0cd5f7a03d498a6d9289773b117590d794a43cd129366fd8e99222e4832f67b1653d54cf + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3319,8 +3497,15 @@ __metadata: "@types/uuid": "npm:^8.3.4" "@types/ws": "npm:^8.5.10" debug: "npm:^4.3.4" + jest: "npm:^28.1.3" + msw: "npm:2.3.0-ws.rc-6" + next: "npm:^14.2.4" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" stack-utils: "npm:^2.0.6" + ts-jest: "npm:^28.0.6" typescript: "npm:^5.4.5" + undici: "npm:^5.28.4" uuid: "npm:^8.3.2" ws: "npm:^8.13.0" peerDependencies: @@ -3382,7 +3567,7 @@ __metadata: text-table: "npm:^0.2.0" ts-jest: "npm:^28.0.6" typescript: "npm:^5.4.5" - ws: "npm:^7.5.0" + undici: "npm:^5.28.4" bin: replay: ./bin.js languageName: unknown @@ -3695,6 +3880,23 @@ __metadata: languageName: node linkType: hard +"@swc/counter@npm:^0.1.3": + version: 0.1.3 + resolution: "@swc/counter@npm:0.1.3" + checksum: 10c0/8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356 + languageName: node + linkType: hard + +"@swc/helpers@npm:0.5.5": + version: 0.5.5 + resolution: "@swc/helpers@npm:0.5.5" + dependencies: + "@swc/counter": "npm:^0.1.3" + tslib: "npm:^2.4.0" + checksum: 10c0/21a9b9cfe7e00865f9c9f3eb4c1cc5b397143464f7abee76a2c5366e591e06b0155b5aac93fe8269ef8d548df253f6fd931e9ddfc0fd12efd405f90f45506e7d + languageName: node + linkType: hard + "@testing-library/dom@npm:^8.0.0": version: 8.20.1 resolution: "@testing-library/dom@npm:8.20.1" @@ -3853,6 +4055,13 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:^0.6.0": + version: 0.6.0 + resolution: "@types/cookie@npm:0.6.0" + checksum: 10c0/5b326bd0188120fb32c0be086b141b1481fec9941b76ad537f9110e10d61ee2636beac145463319c71e4be67a17e85b81ca9e13ceb6e3bb63b93d16824d6c149 + languageName: node + linkType: hard + "@types/debug@npm:^4.1.5, @types/debug@npm:^4.1.7, @types/debug@npm:^4.1.8": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" @@ -4085,6 +4294,15 @@ __metadata: languageName: node linkType: hard +"@types/mute-stream@npm:^0.0.4": + version: 0.0.4 + resolution: "@types/mute-stream@npm:0.0.4" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/944730fd7b398c5078de3c3d4d0afeec8584283bc694da1803fdfca14149ea385e18b1b774326f1601baf53898ce6d121a952c51eb62d188ef6fcc41f725c0dc + languageName: node + linkType: hard + "@types/node-fetch@npm:^2.5.10, @types/node-fetch@npm:^2.6.2, @types/node-fetch@npm:^2.6.3": version: 2.6.11 resolution: "@types/node-fetch@npm:2.6.11" @@ -4292,6 +4510,13 @@ __metadata: languageName: node linkType: hard +"@types/statuses@npm:^2.0.4": + version: 2.0.5 + resolution: "@types/statuses@npm:2.0.5" + checksum: 10c0/4dacec0b29483a44be902a022a11a22b339de7a6e7b2059daa4f7add10cb6dbcc28d02d2a416fe9687e48d335906bf983065391836d4e7c847e55ddef4de8fad + languageName: node + linkType: hard + "@types/string.prototype.matchall@npm:^4.0.0": version: 4.0.4 resolution: "@types/string.prototype.matchall@npm:4.0.4" @@ -4361,6 +4586,13 @@ __metadata: languageName: node linkType: hard +"@types/wrap-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "@types/wrap-ansi@npm:3.0.0" + checksum: 10c0/8d8f53363f360f38135301a06b596c295433ad01debd082078c33c6ed98b05a5c8fe8853a88265432126096084f4a135ec1564e3daad631b83296905509f90b3 + languageName: node + linkType: hard + "@types/ws@npm:^8, @types/ws@npm:^8.5.10, @types/ws@npm:^8.5.5": version: 8.5.10 resolution: "@types/ws@npm:8.5.10" @@ -4986,7 +5218,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.1": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.1, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -5876,6 +6108,15 @@ __metadata: languageName: node linkType: hard +"busboy@npm:1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: "npm:^1.1.0" + checksum: 10c0/fa7e836a2b82699b6e074393428b91ae579d4f9e21f5ac468e1b459a244341d722d2d22d10920cdd849743dbece6dca11d72de939fb75a7448825cf2babfba1f + languageName: node + linkType: hard + "bvaughn-enquirer@npm:2.4.2": version: 2.4.2 resolution: "bvaughn-enquirer@npm:2.4.2" @@ -6009,6 +6250,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001579": + version: 1.0.30001636 + resolution: "caniuse-lite@npm:1.0.30001636" + checksum: 10c0/e5f965b4da7bae1531fd9f93477d015729ff9e3fa12670ead39a9e6cdc4c43e62c272d47857c5cc332e7b02d697cb3f2f965a1030870ac7476da60c2fc81ee94 + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001587": version: 1.0.30001597 resolution: "caniuse-lite@npm:1.0.30001597" @@ -6205,6 +6453,20 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 + languageName: node + linkType: hard + "cliui@npm:^6.0.0": version: 6.0.0 resolution: "cliui@npm:6.0.0" @@ -6514,6 +6776,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:^0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 10c0/c01ca3ef8d7b8187bae434434582288681273b5a9ed27521d4d7f9f7928fe0c920df0decd9f9d3bbd2d14ac432b8c8cf42b98b3bdd5bfe0e6edddeebebe8b61d + languageName: node + linkType: hard + "core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.36.1": version: 3.36.1 resolution: "core-js-compat@npm:3.36.1" @@ -9440,6 +9709,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^16.8.1": + version: 16.8.2 + resolution: "graphql@npm:16.8.2" + checksum: 10c0/921a6321c4efe2540fd5a752093c1542f96cd10b3907b1a8044819f054e6e8a6bca78d93a8eada065a07dfd4e53d89eef99ebdced2c372453e05480e12d8652e + languageName: node + linkType: hard + "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -9541,6 +9817,13 @@ __metadata: languageName: node linkType: hard +"headers-polyfill@npm:^4.0.2": + version: 4.0.3 + resolution: "headers-polyfill@npm:4.0.3" + checksum: 10c0/53e85b2c6385f8d411945fb890c5369f1469ce8aa32a6e8d28196df38568148de640c81cf88cbc7c67767103dd9acba48f4f891982da63178fc6e34560022afe + languageName: node + linkType: hard + "hoopy@npm:^0.1.4": version: 0.1.4 resolution: "hoopy@npm:0.1.4" @@ -10229,6 +10512,13 @@ __metadata: languageName: node linkType: hard +"is-node-process@npm:^1.2.0": + version: 1.2.0 + resolution: "is-node-process@npm:1.2.0" + checksum: 10c0/5b24fda6776d00e42431d7bcd86bce81cb0b6cabeb944142fe7b077a54ada2e155066ad06dbe790abdb397884bdc3151e04a9707b8cd185099efbc79780573ed + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -12585,6 +12875,38 @@ __metadata: languageName: node linkType: hard +"msw@npm:2.3.0-ws.rc-6": + version: 2.3.0-ws.rc-6 + resolution: "msw@npm:2.3.0-ws.rc-6" + dependencies: + "@bundled-es-modules/cookie": "npm:^2.0.0" + "@bundled-es-modules/statuses": "npm:^1.0.1" + "@inquirer/confirm": "npm:^3.0.0" + "@mswjs/cookies": "npm:^1.1.0" + "@mswjs/interceptors": "npm:^0.27.1" + "@open-draft/until": "npm:^2.1.0" + "@types/cookie": "npm:^0.6.0" + "@types/statuses": "npm:^2.0.4" + chalk: "npm:^4.1.2" + graphql: "npm:^16.8.1" + headers-polyfill: "npm:^4.0.2" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.2" + path-to-regexp: "npm:^6.2.0" + strict-event-emitter: "npm:^0.5.1" + type-fest: "npm:^4.9.0" + yargs: "npm:^17.7.2" + peerDependencies: + typescript: ">= 4.7.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 10c0/1ac620e5bce3a193507c14d3cb213f03f0cc6ec11eca849ae3c61c420ae2768d23617569d222bef156ab857afd53012a20208909828e414fe5d39d7b57ca241a + languageName: node + linkType: hard + "multicast-dns@npm:^7.2.5": version: 7.2.5 resolution: "multicast-dns@npm:7.2.5" @@ -12604,6 +12926,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 10c0/dce2a9ccda171ec979a3b4f869a102b1343dee35e920146776780de182f16eae459644d187e38d59a3d37adf85685e1c17c38cf7bfda7e39a9880f7a1d10a74c + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -12615,7 +12944,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -12652,6 +12981,64 @@ __metadata: languageName: node linkType: hard +"next@npm:^14.2.4": + version: 14.2.4 + resolution: "next@npm:14.2.4" + dependencies: + "@next/env": "npm:14.2.4" + "@next/swc-darwin-arm64": "npm:14.2.4" + "@next/swc-darwin-x64": "npm:14.2.4" + "@next/swc-linux-arm64-gnu": "npm:14.2.4" + "@next/swc-linux-arm64-musl": "npm:14.2.4" + "@next/swc-linux-x64-gnu": "npm:14.2.4" + "@next/swc-linux-x64-musl": "npm:14.2.4" + "@next/swc-win32-arm64-msvc": "npm:14.2.4" + "@next/swc-win32-ia32-msvc": "npm:14.2.4" + "@next/swc-win32-x64-msvc": "npm:14.2.4" + "@swc/helpers": "npm:0.5.5" + busboy: "npm:1.6.0" + caniuse-lite: "npm:^1.0.30001579" + graceful-fs: "npm:^4.2.11" + postcss: "npm:8.4.31" + styled-jsx: "npm:5.1.1" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/630c2a197b57c1f29caf4672a0f8fb74dbb048e77e4513f567279467332212f3eebcb68279885f1d525d7aaebbb452f522b02c0b5cd3ca66f385341e4b4eac67 + languageName: node + linkType: hard + "no-case@npm:^3.0.4": version: 3.0.4 resolution: "no-case@npm:3.0.4" @@ -13069,6 +13456,13 @@ __metadata: languageName: node linkType: hard +"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0, outvariant@npm:^1.4.2": + version: 1.4.2 + resolution: "outvariant@npm:1.4.2" + checksum: 10c0/48041425a4cb725ff8871b7d9889bfc2eaded867b9b35b6c2450a36fb3632543173098654990caa6c9e9f67d902b2a01f4402c301835e9ecaf4b4695d3161853 + languageName: node + linkType: hard + "p-filter@npm:^2.1.0": version: 2.1.0 resolution: "p-filter@npm:2.1.0" @@ -13270,6 +13664,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:^6.2.0": + version: 6.2.2 + resolution: "path-to-regexp@npm:6.2.2" + checksum: 10c0/4b60852d3501fd05ca9dd08c70033d73844e5eca14e41f499f069afa8364f780f15c5098002f93bd42af8b3514de62ac6e82a53b5662de881d2b08c9ef21ea6b + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -14211,6 +14612,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + languageName: node + linkType: hard + "postcss@npm:^7.0.35": version: 7.0.39 resolution: "postcss@npm:7.0.39" @@ -14648,6 +15060,18 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:^18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.2" + peerDependencies: + react: ^18.3.1 + checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + languageName: node + linkType: hard + "react-error-overlay@npm:^6.0.11": version: 6.0.11 resolution: "react-error-overlay@npm:6.0.11" @@ -14836,6 +15260,15 @@ __metadata: languageName: node linkType: hard +"react@npm:^18.3.1": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3 + languageName: node + linkType: hard + "read-cache@npm:^1.0.0": version: 1.0.0 resolution: "read-cache@npm:1.0.0" @@ -15072,7 +15505,6 @@ __metadata: prettier: "npm:^2.7.1" replayio: "workspace:^" typescript: "npm:^5.4.5" - ws: "npm:^8.17.0" languageName: unknown linkType: soft @@ -15116,7 +15548,6 @@ __metadata: ts-jest: "npm:^28.0.6" typescript: "npm:^5.4.5" undici: "npm:^5.28.4" - ws: "npm:^7.5.0" bin: replayio: ./replayio.js languageName: unknown @@ -15482,6 +15913,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 + languageName: node + linkType: hard + "schema-utils@npm:2.7.0": version: 2.7.0 resolution: "schema-utils@npm:2.7.0" @@ -15751,7 +16191,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -15927,7 +16367,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0": +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0": version: 1.2.0 resolution: "source-map-js@npm:1.2.0" checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4 @@ -16160,7 +16600,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": +"statuses@npm:2.0.1, statuses@npm:^2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 @@ -16201,6 +16641,20 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 10c0/fbd9aecc2621364384d157f7e59426f4bfd385e8b424b5aaa79c83a6f5a1c8fd2e4e3289e95de1eb3511cb96bb333d6281a9919fafce760e4edb35b2cd2facab + languageName: node + linkType: hard + +"strict-event-emitter@npm:^0.5.1": + version: 0.5.1 + resolution: "strict-event-emitter@npm:0.5.1" + checksum: 10c0/f5228a6e6b6393c57f52f62e673cfe3be3294b35d6f7842fc24b172ae0a6e6c209fa83241d0e433fc267c503bc2f4ffdbe41a9990ff8ffd5ac425ec0489417f7 + languageName: node + linkType: hard + "string-length@npm:^4.0.1": version: 4.0.2 resolution: "string-length@npm:4.0.2" @@ -16443,6 +16897,22 @@ __metadata: languageName: node linkType: hard +"styled-jsx@npm:5.1.1": + version: 5.1.1 + resolution: "styled-jsx@npm:5.1.1" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/42655cdadfa5388f8a48bb282d6b450df7d7b8cf066ac37038bd0499d3c9f084815ebd9ff9dfa12a218fd4441338851db79603498d7557207009c1cf4d609835 + languageName: node + linkType: hard + "stylehacks@npm:^5.1.1": version: 5.1.1 resolution: "stylehacks@npm:5.1.1" @@ -17004,6 +17474,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.4.0": + version: 2.6.3 + resolution: "tslib@npm:2.6.3" + checksum: 10c0/2598aef53d9dbe711af75522464b2104724d6467b26a60f2bdac8297d2b5f1f6b86a71f61717384aa8fd897240467aaa7bcc36a0700a0faf751293d1331db39a + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -17124,6 +17601,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^4.9.0": + version: 4.20.1 + resolution: "type-fest@npm:4.20.1" + checksum: 10c0/c31da16fe170a74c5b7e102e70e764ba8ec6ade128e782a56f842aefa07307df5a6353e6b5601d3b30ff2d856d8b955f89813df639e4758fedcf8e426b2d854e + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -18324,7 +18808,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7.4.6, ws@npm:^7.5.0": +"ws@npm:^7.4.6": version: 7.5.9 resolution: "ws@npm:7.5.9" peerDependencies: @@ -18354,21 +18838,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.17.0": - version: 8.17.0 - resolution: "ws@npm:8.17.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10c0/55241ec93a66fdfc4bf4f8bc66c8eb038fda2c7a4ee8f6f157f2ca7dc7aa76aea0c0da0bf3adb2af390074a70a0e45456a2eaf80e581e630b75df10a64b0a990 - languageName: node - linkType: hard - "xml-name-validator@npm:^3.0.0": version: 3.0.0 resolution: "xml-name-validator@npm:3.0.0" @@ -18492,7 +18961,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1, yargs@npm:^17.7.1": +"yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: