diff --git a/.gitignore b/.gitignore index ae197018..e0efbe5f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules .DS_Store output -.worker-next .open-next .wrangler +.turbo dist \ No newline at end of file diff --git a/examples/e2e/app-router/.gitignore b/examples/e2e/app-router/.gitignore new file mode 100644 index 00000000..61cbd98f --- /dev/null +++ b/examples/e2e/app-router/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +.open-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/examples/e2e/app-router/CHANGELOG.md b/examples/e2e/app-router/CHANGELOG.md new file mode 100644 index 00000000..bc4243e0 --- /dev/null +++ b/examples/e2e/app-router/CHANGELOG.md @@ -0,0 +1,29 @@ +# app-router + +## 0.1.4 + +### Patch Changes + +- Updated dependencies [[`9595714ac23e5f131b879d04d5cfb2a5d11bdbdd`](https://github.com/opennextjs/opennextjs-aws/commit/9595714ac23e5f131b879d04d5cfb2a5d11bdbdd), [`4e88b47935523de1d15da067b56105bd6be91e47`](https://github.com/opennextjs/opennextjs-aws/commit/4e88b47935523de1d15da067b56105bd6be91e47), [`7140ca56e1e88d7a7cae327eceb3ef8c2fde2a1e`](https://github.com/opennextjs/opennextjs-aws/commit/7140ca56e1e88d7a7cae327eceb3ef8c2fde2a1e)]: + - @opennextjs/aws@3.3.1 + +## 0.1.3 + +### Patch Changes + +- Updated dependencies [[`4d328e3fc306b878e9497986baa65bfd1d4de66a`](https://github.com/opennextjs/opennextjs-aws/commit/4d328e3fc306b878e9497986baa65bfd1d4de66a), [`2b2a48b70ae95b5e600ac2e4b7f2df8702c5c26e`](https://github.com/opennextjs/opennextjs-aws/commit/2b2a48b70ae95b5e600ac2e4b7f2df8702c5c26e), [`f685ddea8f8a5c82591dc02713aff7138f2d9896`](https://github.com/opennextjs/opennextjs-aws/commit/f685ddea8f8a5c82591dc02713aff7138f2d9896), [`ef1fe48d570863266c271e5dedaf02b943849ded`](https://github.com/opennextjs/opennextjs-aws/commit/ef1fe48d570863266c271e5dedaf02b943849ded), [`8ab921f8b5bd40c7ba109ccef3e59a6c24283fb2`](https://github.com/opennextjs/opennextjs-aws/commit/8ab921f8b5bd40c7ba109ccef3e59a6c24283fb2), [`2202f36ce0f87357b249bd127cdd5e84d6deffd3`](https://github.com/opennextjs/opennextjs-aws/commit/2202f36ce0f87357b249bd127cdd5e84d6deffd3), [`44392ba82990d43e16a614113d9e7d8e257e5bdd`](https://github.com/opennextjs/opennextjs-aws/commit/44392ba82990d43e16a614113d9e7d8e257e5bdd), [`4dea7ea2f5ffd1848e51502c88d2efcc1896bb8c`](https://github.com/opennextjs/opennextjs-aws/commit/4dea7ea2f5ffd1848e51502c88d2efcc1896bb8c), [`0ac604e5867497cc93fb677b5ebc28ef87e057f8`](https://github.com/opennextjs/opennextjs-aws/commit/0ac604e5867497cc93fb677b5ebc28ef87e057f8), [`1ece6b479bb4e0309892ffbd1200870821a410c4`](https://github.com/opennextjs/opennextjs-aws/commit/1ece6b479bb4e0309892ffbd1200870821a410c4), [`697681bf9ce25212ce4e2e94d886ca425428280d`](https://github.com/opennextjs/opennextjs-aws/commit/697681bf9ce25212ce4e2e94d886ca425428280d)]: + - @opennextjs/aws@3.3.0 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [[`6f798debb575b157acb2f5068658f95ace0fae50`](https://github.com/opennextjs/opennextjs-aws/commit/6f798debb575b157acb2f5068658f95ace0fae50), [`fe600ac6f5e513376cf233a5d2ce68affaa3aa5a`](https://github.com/opennextjs/opennextjs-aws/commit/fe600ac6f5e513376cf233a5d2ce68affaa3aa5a), [`5f0cbc8feac9eec728c27bb3b7ff5c3f3bc26716`](https://github.com/opennextjs/opennextjs-aws/commit/5f0cbc8feac9eec728c27bb3b7ff5c3f3bc26716), [`8b51108d9aee7e5ed3027c1ceda99091b579951d`](https://github.com/opennextjs/opennextjs-aws/commit/8b51108d9aee7e5ed3027c1ceda99091b579951d), [`b999c4e9a38499680bed77ddeb94b62a3301c0fa`](https://github.com/opennextjs/opennextjs-aws/commit/b999c4e9a38499680bed77ddeb94b62a3301c0fa), [`ba84259d2e35e79a562a7e3f055e350a03c9d651`](https://github.com/opennextjs/opennextjs-aws/commit/ba84259d2e35e79a562a7e3f055e350a03c9d651)]: + - @opennextjs/aws@3.2.2 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [[`cf33973f3fbab73e77898fdd072a00a1f037257a`](https://github.com/opennextjs/opennextjs-aws/commit/cf33973f3fbab73e77898fdd072a00a1f037257a), [`77d87e7a870fad6afad022bf75aca18c8656c268`](https://github.com/opennextjs/opennextjs-aws/commit/77d87e7a870fad6afad022bf75aca18c8656c268), [`a43b82b4cb68889371ac8260aefef9e04eefb037`](https://github.com/opennextjs/opennextjs-aws/commit/a43b82b4cb68889371ac8260aefef9e04eefb037), [`bfa1a8c4056bd691fb57617dd6287693e51071b4`](https://github.com/opennextjs/opennextjs-aws/commit/bfa1a8c4056bd691fb57617dd6287693e51071b4), [`5839217411012d1df2874d299daa977ba3701c2c`](https://github.com/opennextjs/opennextjs-aws/commit/5839217411012d1df2874d299daa977ba3701c2c), [`dfc174d88b7bcc54eede09c98d9443dd84b93fd8`](https://github.com/opennextjs/opennextjs-aws/commit/dfc174d88b7bcc54eede09c98d9443dd84b93fd8)]: + - @opennextjs/aws@3.2.1 diff --git a/examples/e2e/app-router/README.md b/examples/e2e/app-router/README.md new file mode 100644 index 00000000..f4b9da05 --- /dev/null +++ b/examples/e2e/app-router/README.md @@ -0,0 +1,3 @@ +# App Router + +This project uses the App Router exclusively... diff --git a/examples/e2e/app-router/app/albums/@modal/(.)[album]/[song]/page.tsx b/examples/e2e/app-router/app/albums/@modal/(.)[album]/[song]/page.tsx new file mode 100644 index 00000000..f83f84b4 --- /dev/null +++ b/examples/e2e/app-router/app/albums/@modal/(.)[album]/[song]/page.tsx @@ -0,0 +1,29 @@ +import { getSong } from "@example/shared/api"; +import Modal from "@example/shared/components/Modal"; + +type Props = { + params: Promise<{ + album: string; + song: string; + }>; +}; +export default async function SongPage(props: Props) { + const params = await props.params; + const song = await getSong(params.album, params.song); + return ( + +

Modal

+ Album: {decodeURIComponent(params.album)} +
+ {/*
+
+ ); +} diff --git a/examples/e2e/app-router/app/albums/@modal/(.)[album]/page.tsx b/examples/e2e/app-router/app/albums/@modal/(.)[album]/page.tsx new file mode 100644 index 00000000..463c0c96 --- /dev/null +++ b/examples/e2e/app-router/app/albums/@modal/(.)[album]/page.tsx @@ -0,0 +1,11 @@ +import Modal from "@example/shared/components/Modal"; + +type Props = { + params: Promise<{ + artist: string; + }>; +}; +export default async function ArtistPage(props: Props) { + const params = await props.params; + return Artists {params.artist}; +} diff --git a/examples/e2e/app-router/app/albums/@modal/default.tsx b/examples/e2e/app-router/app/albums/@modal/default.tsx new file mode 100644 index 00000000..6ddf1b76 --- /dev/null +++ b/examples/e2e/app-router/app/albums/@modal/default.tsx @@ -0,0 +1,3 @@ +export default function Default() { + return null; +} diff --git a/examples/e2e/app-router/app/albums/[album]/[song]/page.tsx b/examples/e2e/app-router/app/albums/[album]/[song]/page.tsx new file mode 100644 index 00000000..87e15694 --- /dev/null +++ b/examples/e2e/app-router/app/albums/[album]/[song]/page.tsx @@ -0,0 +1,25 @@ +import { getSong } from "@example/shared/api"; + +type Props = { + params: Promise<{ + album: string; + song: string; + }>; +}; +export default async function Song(props: Props) { + const params = await props.params; + const song = await getSong(params.album, params.song); + + return ( +
+

Not Modal

+ {decodeURIComponent(params.album)} + +
+ ); +} diff --git a/examples/e2e/app-router/app/albums/[album]/page.tsx b/examples/e2e/app-router/app/albums/[album]/page.tsx new file mode 100644 index 00000000..8d3a5537 --- /dev/null +++ b/examples/e2e/app-router/app/albums/[album]/page.tsx @@ -0,0 +1,3 @@ +export default function ArtistPage() { + return
Artist
; +} diff --git a/examples/e2e/app-router/app/albums/layout.tsx b/examples/e2e/app-router/app/albums/layout.tsx new file mode 100644 index 00000000..0de9cfdc --- /dev/null +++ b/examples/e2e/app-router/app/albums/layout.tsx @@ -0,0 +1,10 @@ +import type { ReactNode } from "react"; + +export default function Layout({ children, modal }: { children: ReactNode; modal: ReactNode }) { + return ( +
+ {children} + {modal} +
+ ); +} diff --git a/examples/e2e/app-router/app/albums/page.tsx b/examples/e2e/app-router/app/albums/page.tsx new file mode 100644 index 00000000..6471009b --- /dev/null +++ b/examples/e2e/app-router/app/albums/page.tsx @@ -0,0 +1,13 @@ +import { getAlbums } from "@example/shared/api"; +import Album from "@example/shared/components/Album"; + +export default async function AlbumPage() { + const albums = await getAlbums(); + return ( +
+ {albums.map((album) => ( + + ))} +
+ ); +} diff --git a/examples/e2e/app-router/app/api/after/revalidate/route.ts b/examples/e2e/app-router/app/api/after/revalidate/route.ts new file mode 100644 index 00000000..2bc059fc --- /dev/null +++ b/examples/e2e/app-router/app/api/after/revalidate/route.ts @@ -0,0 +1,16 @@ +import { revalidateTag } from "next/cache"; +import { NextResponse, after } from "next/server"; + +export function POST() { + after( + () => + new Promise((resolve) => + setTimeout(() => { + revalidateTag("date"); + resolve(); + }, 5000) + ) + ); + + return NextResponse.json({ success: true }); +} diff --git a/examples/e2e/app-router/app/api/after/ssg/route.ts b/examples/e2e/app-router/app/api/after/ssg/route.ts new file mode 100644 index 00000000..2acf353f --- /dev/null +++ b/examples/e2e/app-router/app/api/after/ssg/route.ts @@ -0,0 +1,12 @@ +import { unstable_cache } from "next/cache"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-static"; + +export async function GET() { + const dateFn = unstable_cache(() => new Date().toISOString(), ["date"], { + tags: ["date"], + }); + const date = await dateFn(); + return NextResponse.json({ date }); +} diff --git a/examples/e2e/app-router/app/api/client/route.ts b/examples/e2e/app-router/app/api/client/route.ts new file mode 100644 index 00000000..e9c204ba --- /dev/null +++ b/examples/e2e/app-router/app/api/client/route.ts @@ -0,0 +1,7 @@ +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + return NextResponse.json({ + hello: "client", + }); +} diff --git a/examples/e2e/app-router/app/api/host/route.ts b/examples/e2e/app-router/app/api/host/route.ts new file mode 100644 index 00000000..16630b34 --- /dev/null +++ b/examples/e2e/app-router/app/api/host/route.ts @@ -0,0 +1,7 @@ +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + return NextResponse.json({ + url: request.url, + }); +} diff --git a/examples/e2e/app-router/app/api/isr/route.ts b/examples/e2e/app-router/app/api/isr/route.ts new file mode 100644 index 00000000..1fc3e43d --- /dev/null +++ b/examples/e2e/app-router/app/api/isr/route.ts @@ -0,0 +1,27 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +// This endpoint simulates an on demand revalidation request +export async function GET(request: NextRequest) { + const cwd = process.cwd(); + const prerenderManifest = await fs.readFile(path.join(cwd, ".next/prerender-manifest.json"), "utf-8"); + const manifest = JSON.parse(prerenderManifest); + const previewId = manifest.preview.previewModeId; + + const result = await fetch(`https://${request.headers.get("host")}/isr`, { + headers: { "x-prerender-revalidate": previewId }, + method: "HEAD", + }); + + return NextResponse.json({ + status: 200, + body: { + result: result.ok, + cacheControl: result.headers.get("cache-control"), + }, + }); +} diff --git a/examples/e2e/app-router/app/api/og/route.tsx b/examples/e2e/app-router/app/api/og/route.tsx new file mode 100644 index 00000000..b2263de5 --- /dev/null +++ b/examples/e2e/app-router/app/api/og/route.tsx @@ -0,0 +1,72 @@ +import { ImageResponse } from "next/og"; +// App router includes @vercel/og. +// No need to install it. +// ?title= + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + + // ?title=<title> + const hasTitle = searchParams.has("title"); + const title = hasTitle ? searchParams.get("title")?.slice(0, 100) : "My default title"; + + return new ImageResponse( + ( + <div + style={{ + backgroundColor: "black", + backgroundSize: "150px 150px", + height: "100%", + width: "100%", + display: "flex", + textAlign: "center", + alignItems: "center", + justifyContent: "center", + flexDirection: "column", + flexWrap: "nowrap", + }} + > + <div + style={{ + display: "flex", + alignItems: "center", + justifyContent: "center", + justifyItems: "center", + }} + > + <img + alt="Vercel" + height={200} + src="data:image/svg+xml,%3Csvg width='116' height='100' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M57.5 0L115 100H0L57.5 0z' /%3E%3C/svg%3E" + style={{ margin: "0 30px" }} + width={232} + /> + </div> + <div + style={{ + fontSize: 60, + fontStyle: "normal", + letterSpacing: "-0.025em", + color: "white", + marginTop: 30, + padding: "0 120px", + lineHeight: 1.4, + whiteSpace: "pre-wrap", + }} + > + {title} + </div> + </div> + ), + { + width: 1200, + height: 630, + } + ); + } catch (e: any) { + return new Response("Failed to generate the image", { + status: 500, + }); + } +} diff --git a/examples/e2e/app-router/app/api/page.tsx b/examples/e2e/app-router/app/api/page.tsx new file mode 100644 index 00000000..0771ac5a --- /dev/null +++ b/examples/e2e/app-router/app/api/page.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useCallback, useState } from "react"; + +/** + * Make /api/hello call exclusively on the client + * - we already know SSR can fetch itself w/o issues + */ +export default function Page() { + const [data, setData] = useState(); + + const onClientClick = useCallback(async () => { + const { protocol, host } = window.location; + const url = `${protocol}//${host}`; + const r = await fetch(`${url}/api/client`); + const d = await r.json(); + setData(d); + }, []); + + const onMiddlewareClick = useCallback(async () => { + const { protocol, host } = window.location; + const url = `${protocol}//${host}`; + const r = await fetch(`${url}/api/middleware`); + const d = await r.json(); + setData(d); + }, []); + + return ( + <div> + <div>API: {data ? JSON.stringify(data, null, 2) : "N/A"}</div> + + <button className="border p-2" onClick={onClientClick}> + Call /api/client + </button> + <button className="border p-2" onClick={onMiddlewareClick}> + Call /api/middleware + </button> + </div> + ); +} diff --git a/examples/e2e/app-router/app/api/revalidate-path/route.ts b/examples/e2e/app-router/app/api/revalidate-path/route.ts new file mode 100644 index 00000000..625d5a65 --- /dev/null +++ b/examples/e2e/app-router/app/api/revalidate-path/route.ts @@ -0,0 +1,9 @@ +import { revalidatePath } from "next/cache"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + revalidatePath("/revalidate-path"); + + return new Response("ok"); +} diff --git a/examples/e2e/app-router/app/api/revalidate-tag/route.ts b/examples/e2e/app-router/app/api/revalidate-tag/route.ts new file mode 100644 index 00000000..e0346d42 --- /dev/null +++ b/examples/e2e/app-router/app/api/revalidate-tag/route.ts @@ -0,0 +1,9 @@ +import { revalidateTag } from "next/cache"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + revalidateTag("revalidate"); + + return new Response("ok"); +} diff --git a/examples/e2e/app-router/app/api/sse/route.ts b/examples/e2e/app-router/app/api/sse/route.ts new file mode 100644 index 00000000..645b24f5 --- /dev/null +++ b/examples/e2e/app-router/app/api/sse/route.ts @@ -0,0 +1,52 @@ +import type { NextRequest } from "next/server"; + +function wait(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +export const dynamic = "force-dynamic"; + +export async function GET(request: NextRequest) { + const resStream = new TransformStream(); + const writer = resStream.writable.getWriter(); + + const res = new Response(resStream.readable, { + headers: { + "Content-Type": "text/event-stream", + Connection: "keep-alive", + "Cache-Control": "no-cache, no-transform", + }, + }); + + setTimeout(async () => { + await writer.write( + `data: ${JSON.stringify({ + message: "open", + time: new Date().toISOString(), + })}\n\n` + ); + for (let i = 1; i <= 4; i++) { + await wait(2000); + await writer.write( + `data: ${JSON.stringify({ + message: `hello:${i}`, + time: new Date().toISOString(), + })}\n\n` + ); + } + + await wait(2000); // Wait for 4 seconds + await writer.write( + `data: ${JSON.stringify({ + message: "close", + time: new Date().toISOString(), + })}\n\n` + ); + await wait(5000); + await writer.close(); + }, 100); + + return res; +} diff --git a/examples/e2e/app-router/app/config-redirect/page.tsx b/examples/e2e/app-router/app/config-redirect/page.tsx new file mode 100644 index 00000000..6e51d3d5 --- /dev/null +++ b/examples/e2e/app-router/app/config-redirect/page.tsx @@ -0,0 +1,8 @@ +export default function RedirectDestination() { + return ( + <div> + <h1>I was redirected from next.config.js</h1> + <p>/next-config-redirect => /config-redirect</p> + </div> + ); +} diff --git a/examples/e2e/app-router/app/globals.css b/examples/e2e/app-router/app/globals.css new file mode 100644 index 00000000..e0936eda --- /dev/null +++ b/examples/e2e/app-router/app/globals.css @@ -0,0 +1,23 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) + rgb(var(--background-start-rgb)); +} diff --git a/examples/e2e/app-router/app/headers/page.tsx b/examples/e2e/app-router/app/headers/page.tsx new file mode 100644 index 00000000..643a5b8c --- /dev/null +++ b/examples/e2e/app-router/app/headers/page.tsx @@ -0,0 +1,11 @@ +import { headers } from "next/headers"; + +export default async function Headers() { + const middlewareHeader = (await headers()).get("request-header"); + return ( + <div> + <h1>Headers</h1> + <div>{middlewareHeader}</div> + </div> + ); +} diff --git a/examples/e2e/app-router/app/image-optimization/page.tsx b/examples/e2e/app-router/app/image-optimization/page.tsx new file mode 100644 index 00000000..05e17139 --- /dev/null +++ b/examples/e2e/app-router/app/image-optimization/page.tsx @@ -0,0 +1,14 @@ +import Image from "next/image"; + +export default function ImageOptimization() { + return ( + <div> + <Image + src="https://opennext.js.org/architecture.png" + alt="Open Next architecture" + width={300} + height={300} + /> + </div> + ); +} diff --git a/examples/e2e/app-router/app/isr/page.tsx b/examples/e2e/app-router/app/isr/page.tsx new file mode 100644 index 00000000..3eadac36 --- /dev/null +++ b/examples/e2e/app-router/app/isr/page.tsx @@ -0,0 +1,9 @@ +async function getTime() { + return new Date().toISOString(); +} + +export const revalidate = 10; +export default async function ISR() { + const time = getTime(); + return <div>Time: {time}</div>; +} diff --git a/examples/e2e/app-router/app/layout.tsx b/examples/e2e/app-router/app/layout.tsx new file mode 100644 index 00000000..d06375b3 --- /dev/null +++ b/examples/e2e/app-router/app/layout.tsx @@ -0,0 +1,19 @@ +import "./globals.css"; + +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Nextjs App Router", + description: "Generated by create next app", +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + <html lang="en"> + <body className={inter.className}>{children}</body> + </html> + ); +} diff --git a/examples/e2e/app-router/app/og/opengraph-image.tsx b/examples/e2e/app-router/app/og/opengraph-image.tsx new file mode 100644 index 00000000..7539eb4d --- /dev/null +++ b/examples/e2e/app-router/app/og/opengraph-image.tsx @@ -0,0 +1,38 @@ +import { ImageResponse } from "next/og"; + +// Image metadata +export const alt = "OpenNext"; +export const size = { + width: 1200, + height: 630, +}; + +export const contentType = "image/png"; + +// Image generation +export default async function Image() { + return new ImageResponse( + ( + // ImageResponse JSX element + <div + style={{ + fontSize: 128, + background: "white", + width: "100%", + height: "100%", + display: "flex", + alignItems: "center", + justifyContent: "center", + }} + > + OpenNext + </div> + ), + // ImageResponse options + { + // For convenience, we can re-use the exported opengraph-image + // size config to also set the ImageResponse's width and height. + ...size, + } + ); +} diff --git a/examples/e2e/app-router/app/og/page.tsx b/examples/e2e/app-router/app/og/page.tsx new file mode 100644 index 00000000..0b98f2d6 --- /dev/null +++ b/examples/e2e/app-router/app/og/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return <div></div>; +} diff --git a/examples/e2e/app-router/app/page.tsx b/examples/e2e/app-router/app/page.tsx new file mode 100644 index 00000000..7480a490 --- /dev/null +++ b/examples/e2e/app-router/app/page.tsx @@ -0,0 +1,49 @@ +import Nav from "@example/shared/components/Nav"; + +export default function Home() { + return ( + <> + <h1>Nextjs App Router</h1> + + <main className="grid grid-cols-2 gap-4 p-10 [&>a]:border"> + <Nav href={"/albums"} title="Albums"> + Modal and interception of the greatest hits + </Nav> + <Nav href={"/rewrite"} title="Rewrite"> + Middleware Rewrite of a page. /rewrite should rewrite the contents of /rewrite-destination + </Nav> + <Nav href={"/redirect"} title="Redirect"> + Middleware Rewrite of a page. /redirect should redirect page to /redirect-destination + </Nav> + <Nav href={"/server-actions"} title="Server Actions"> + Client component imports a 'use server' server action and calls it directly without setting up any + api endpoints + </Nav> + <Nav href={"/isr"} title="ISR"> + Incremental Static Regeneration revalidates every 10 seconds with a new timestamp + </Nav> + <Nav href={"/ssr"} title="SSR"> + Server Side Render should generate a new timestamp on each load. Streaming support for loading... + </Nav> + <Nav href={"/api"} title="API"> + Calls an API endpoint defined in app/api/hello/route and middleware + </Nav> + <Nav href={"/parallel"} title="Parallel"> + Parallel routing + </Nav> + <Nav href={"/headers"} title="Headers"> + Headers from middleware should be available via headers() + </Nav> + <Nav href={"/search-query"} title="Search Query"> + Search Query Params should be available in middleware + </Nav> + <Nav href={"/sse"} title="Server Sent Events"> + Server Sent Events via Streaming + </Nav> + <Nav href={"/image-optimization"} title="Image Optimization"> + Image Optimization with next/image + </Nav> + </main> + </> + ); +} diff --git a/examples/e2e/app-router/app/parallel/@a/a-page/page.tsx b/examples/e2e/app-router/app/parallel/@a/a-page/page.tsx new file mode 100644 index 00000000..8108d277 --- /dev/null +++ b/examples/e2e/app-router/app/parallel/@a/a-page/page.tsx @@ -0,0 +1,3 @@ +export default function APage() { + return <div>A Page</div>; +} diff --git a/examples/e2e/app-router/app/parallel/@a/page.tsx b/examples/e2e/app-router/app/parallel/@a/page.tsx new file mode 100644 index 00000000..5e7ee146 --- /dev/null +++ b/examples/e2e/app-router/app/parallel/@a/page.tsx @@ -0,0 +1,10 @@ +import Link from "next/link"; + +export default function A() { + return ( + <div className="border p-4"> + <h1>Parallel Route A</h1> + <Link href="/parallel/a-page">Go to a-page</Link> + </div> + ); +} diff --git a/examples/e2e/app-router/app/parallel/@b/b-page/page.tsx b/examples/e2e/app-router/app/parallel/@b/b-page/page.tsx new file mode 100644 index 00000000..1750356e --- /dev/null +++ b/examples/e2e/app-router/app/parallel/@b/b-page/page.tsx @@ -0,0 +1,3 @@ +export default function BPage() { + return <div>B Page</div>; +} diff --git a/examples/e2e/app-router/app/parallel/@b/page.tsx b/examples/e2e/app-router/app/parallel/@b/page.tsx new file mode 100644 index 00000000..b217f84e --- /dev/null +++ b/examples/e2e/app-router/app/parallel/@b/page.tsx @@ -0,0 +1,11 @@ +import Link from "next/link"; + +export default function B() { + return ( + <div className="border p-4"> + <h1>Parallel Route B</h1> + + <Link href="/parallel/b-page">Go to b-page</Link> + </div> + ); +} diff --git a/examples/e2e/app-router/app/parallel/layout.tsx b/examples/e2e/app-router/app/parallel/layout.tsx new file mode 100644 index 00000000..5925ac09 --- /dev/null +++ b/examples/e2e/app-router/app/parallel/layout.tsx @@ -0,0 +1,42 @@ +"use client"; +import { useState } from "react"; + +import type { ReactNode } from "react"; + +export default function Layout({ a, b, children }: { children: ReactNode; a: ReactNode; b: ReactNode }) { + const [routeA, setRouteA] = useState(false); + const [routeB, setRouteB] = useState(false); + + return ( + <div> + <div className="flex flex-col mb-10"> + <label htmlFor="a"> + Enable A + <input + name="a" + type="checkbox" + checked={routeA} + onChange={(e) => { + setRouteA(e.target.checked); + }} + /> + </label> + <label htmlFor="b"> + Enable B + <input + name="b" + type="checkbox" + checked={routeB} + onChange={(e) => { + setRouteB(e.target.checked); + }} + /> + </label> + </div> + + {routeA && a} + {routeB && b} + {/* {children} */} + </div> + ); +} diff --git a/examples/e2e/app-router/app/parallel/page.tsx b/examples/e2e/app-router/app/parallel/page.tsx new file mode 100644 index 00000000..67e08591 --- /dev/null +++ b/examples/e2e/app-router/app/parallel/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return null; +} diff --git a/examples/e2e/app-router/app/redirect-destination/page.tsx b/examples/e2e/app-router/app/redirect-destination/page.tsx new file mode 100644 index 00000000..1fe01ac3 --- /dev/null +++ b/examples/e2e/app-router/app/redirect-destination/page.tsx @@ -0,0 +1,3 @@ +export default function RedirectDestination() { + return <div>Redirect Destination</div>; +} diff --git a/examples/e2e/app-router/app/revalidate-path/page.tsx b/examples/e2e/app-router/app/revalidate-path/page.tsx new file mode 100644 index 00000000..8a1f9de4 --- /dev/null +++ b/examples/e2e/app-router/app/revalidate-path/page.tsx @@ -0,0 +1,19 @@ +export default async function Page() { + const responseSST = await fetch("https://sst.dev", { + next: { + tags: ["path"], + }, + }); + // This one doesn't have a tag + const responseOpenNext = await fetch("https://opennext.js.org"); + const reqIdSst = responseSST.headers.get("x-amz-cf-id"); + const dateInOpenNext = responseOpenNext.headers.get("date"); + return ( + <div> + <h1>Request id from SST</h1> + <p>RequestID: {reqIdSst}</p> + <h1>Date from from OpenNext</h1> + <p>Date: {dateInOpenNext}</p> + </div> + ); +} diff --git a/examples/e2e/app-router/app/revalidate-tag/layout.tsx b/examples/e2e/app-router/app/revalidate-tag/layout.tsx new file mode 100644 index 00000000..8c309316 --- /dev/null +++ b/examples/e2e/app-router/app/revalidate-tag/layout.tsx @@ -0,0 +1,15 @@ +import { unstable_cache } from "next/cache"; +import type { ReactNode } from "react"; + +export default async function Layout({ children }: { children: ReactNode }) { + const fakeFetch = unstable_cache(async () => new Date().getTime(), ["fakeFetch"], { + tags: ["revalidate"], + }); + const fetchedDate = await fakeFetch(); + return ( + <div> + <div>Fetched time: {new Date(fetchedDate).toISOString()}</div> + {children} + </div> + ); +} diff --git a/examples/e2e/app-router/app/revalidate-tag/nested/page.tsx b/examples/e2e/app-router/app/revalidate-tag/nested/page.tsx new file mode 100644 index 00000000..8436502b --- /dev/null +++ b/examples/e2e/app-router/app/revalidate-tag/nested/page.tsx @@ -0,0 +1,3 @@ +export default async function Nested() { + return <div>Nested</div>; +} diff --git a/examples/e2e/app-router/app/revalidate-tag/page.tsx b/examples/e2e/app-router/app/revalidate-tag/page.tsx new file mode 100644 index 00000000..c07794e7 --- /dev/null +++ b/examples/e2e/app-router/app/revalidate-tag/page.tsx @@ -0,0 +1,8 @@ +async function getTime() { + return new Date().toISOString(); +} + +export default async function ISR() { + const time = getTime(); + return <div>Time: {time}</div>; +} diff --git a/examples/e2e/app-router/app/rewrite-destination/page.tsx b/examples/e2e/app-router/app/rewrite-destination/page.tsx new file mode 100644 index 00000000..c7ea97c3 --- /dev/null +++ b/examples/e2e/app-router/app/rewrite-destination/page.tsx @@ -0,0 +1,3 @@ +export default function RewriteDestination() { + return <div>Rewritten Destination</div>; +} diff --git a/examples/e2e/app-router/app/search-query/page.tsx b/examples/e2e/app-router/app/search-query/page.tsx new file mode 100644 index 00000000..922f018d --- /dev/null +++ b/examples/e2e/app-router/app/search-query/page.tsx @@ -0,0 +1,25 @@ +import { headers } from "next/headers"; + +export default async function SearchQuery(props: { + searchParams: Promise<Record<string, string | string[]>>; +}) { + const propsSearchParams = await props.searchParams; + const mwSearchParams = (await headers()).get("search-params"); + const multiValueParams = propsSearchParams.multi; + const multiValueArray = Array.isArray(multiValueParams) ? multiValueParams : [multiValueParams]; + return ( + <> + <h1>Search Query</h1> + <div>Search Params via Props: {propsSearchParams.searchParams}</div> + <div>Search Params via Middleware: {mwSearchParams}</div> + {multiValueParams && ( + <> + <div>Multi-value Params (key: multi): {multiValueArray.length}</div> + {multiValueArray.map((value) => ( + <div>{value}</div> + ))} + </> + )} + </> + ); +} diff --git a/examples/e2e/app-router/app/server-actions/client.tsx b/examples/e2e/app-router/app/server-actions/client.tsx new file mode 100644 index 00000000..8f3bbda3 --- /dev/null +++ b/examples/e2e/app-router/app/server-actions/client.tsx @@ -0,0 +1,26 @@ +"use client"; +import { useCallback, useState, useTransition } from "react"; + +import type { Song as SongType } from "@example/shared/api"; +import { getSong } from "@example/shared/api"; +import Song from "@example/shared/components/Album/Song"; + +export default function Client() { + const [isPending, startTransition] = useTransition(); + const [song, setSong] = useState<SongType>(); + + const onClick = useCallback(() => { + startTransition(async () => { + const song = await getSong("Hold Me In Your Arms", "I'm never gonna give you up"); + setSong(song); + }); + }, []); + + return ( + <div> + <button onClick={onClick}>Fire Server Actions</button> + {isPending && <div>☎️ing Server Actions...</div>} + {song && <Song song={song} play />} + </div> + ); +} diff --git a/examples/e2e/app-router/app/server-actions/page.tsx b/examples/e2e/app-router/app/server-actions/page.tsx new file mode 100644 index 00000000..f15c66fc --- /dev/null +++ b/examples/e2e/app-router/app/server-actions/page.tsx @@ -0,0 +1,10 @@ +import Client from "./client"; + +export default function Page() { + return ( + <div> + <h1>Server Actions</h1> + <Client /> + </div> + ); +} diff --git a/examples/e2e/app-router/app/sse/page.tsx b/examples/e2e/app-router/app/sse/page.tsx new file mode 100644 index 00000000..76e5582c --- /dev/null +++ b/examples/e2e/app-router/app/sse/page.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { useEffect, useState } from "react"; + +export default function SSE() { + const [events, setEvents] = useState<any[]>([]); + + useEffect(() => { + const e = new EventSource("/api/sse"); + + e.onmessage = (msg) => { + console.log(msg); + try { + const data = JSON.parse(msg.data); + if (data.message === "close") { + e.close(); + console.log("closing"); + } + setEvents((prev) => prev.concat(data)); + } catch (err) { + console.log("failed to parse: ", err, msg); + } + }; + }, []); + + return ( + <> + <h1>Server Sent Event</h1> + {events.map((e, i) => ( + <div key={i}> + Message {i}: {JSON.stringify(e)} + </div> + ))} + </> + ); +} diff --git a/examples/e2e/app-router/app/ssr/layout.tsx b/examples/e2e/app-router/app/ssr/layout.tsx new file mode 100644 index 00000000..181825b1 --- /dev/null +++ b/examples/e2e/app-router/app/ssr/layout.tsx @@ -0,0 +1,12 @@ +import type { PropsWithChildren } from "react"; + +export default function Layout({ children }: PropsWithChildren) { + return ( + <div> + <h1>SSR</h1> + {/* 16 kb seems necessary here to prevent any buffering*/} + {/* <Filler size={16} /> */} + {children} + </div> + ); +} diff --git a/examples/e2e/app-router/app/ssr/loading.tsx b/examples/e2e/app-router/app/ssr/loading.tsx new file mode 100644 index 00000000..fc80ef06 --- /dev/null +++ b/examples/e2e/app-router/app/ssr/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return <div>Loading...</div>; +} diff --git a/examples/e2e/app-router/app/ssr/page.tsx b/examples/e2e/app-router/app/ssr/page.tsx new file mode 100644 index 00000000..563c4483 --- /dev/null +++ b/examples/e2e/app-router/app/ssr/page.tsx @@ -0,0 +1,25 @@ +import { headers } from "next/headers"; + +async function getTime() { + const res = await new Promise<string>((resolve) => { + setTimeout(() => { + resolve(new Date().toISOString()); + }, 1500); + }); + return res; +} + +export default async function SSR() { + const time = await getTime(); + const headerList = await headers(); + const responseOpenNext = await fetch("https://opennext.js.org", { + cache: "force-cache", + }); + return ( + <div> + <h1>Time: {time}</h1> + <div> {headerList.get("host")}</div> + <p>Cached fetch: {responseOpenNext.headers.get("date")}</p> + </div> + ); +} diff --git a/examples/e2e/app-router/middleware.ts b/examples/e2e/app-router/middleware.ts new file mode 100644 index 00000000..923fcaee --- /dev/null +++ b/examples/e2e/app-router/middleware.ts @@ -0,0 +1,71 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +export function middleware(request: NextRequest) { + const path = request.nextUrl.pathname; //new URL(request.url).pathname; + + const host = request.headers.get("host"); + const protocol = host?.startsWith("localhost") ? "http" : "https"; + if (path === "/redirect") { + const u = new URL("/redirect-destination", `${protocol}://${host}`); + return NextResponse.redirect(u, { + headers: { "set-cookie": "test=success" }, + }); + } + if (path === "/rewrite") { + const u = new URL("/rewrite-destination", `${protocol}://${host}`); + return NextResponse.rewrite(u); + } + if (path === "/api/middleware") { + return new NextResponse(JSON.stringify({ hello: "middleware" }), { + status: 200, + headers: { + "content-type": "application/json", + }, + }); + } + const requestHeaders = new Headers(); + // Setting the Request Headers, this should be available in RSC + requestHeaders.set("request-header", "request-header"); + requestHeaders.set("search-params", `mw/${request.nextUrl.searchParams.get("searchParams") || ""}`); + const responseHeaders = new Headers(); + // Response headers should show up in the client's response headers + responseHeaders.set("response-header", "response-header"); + + // Set the cache control header with custom swr + // For: isr.test.ts + if (path === "/isr" && !request.headers.get("x-prerender-revalidate")) { + responseHeaders.set("cache-control", "max-age=10, stale-while-revalidate=999"); + } + + // It is so that cloudfront doesn't cache the response + if ( + path.startsWith("/revalidate-tag") || + path.startsWith("/revalidate-path") || + path.startsWith("/api/after/ssg") + ) { + responseHeaders.set("cache-control", "private, no-cache, no-store, max-age=0, must-revalidate"); + } + + const r = NextResponse.next({ + headers: responseHeaders, + request: { + headers: requestHeaders, + }, + }); + + // Set cookies in middleware + // For: middleware.cookies.test.ts + r.cookies.set("from", "middleware", { + expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), + }); + r.cookies.set("with", "love", { + expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), + }); + + return r; +} + +export const config = { + matcher: ["/((?!_next|favicon.ico|match|static|fonts|api/auth|og).*)"], +}; diff --git a/examples/e2e/app-router/next.config.ts b/examples/e2e/app-router/next.config.ts new file mode 100644 index 00000000..8a85d80a --- /dev/null +++ b/examples/e2e/app-router/next.config.ts @@ -0,0 +1,76 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + poweredByHeader: false, + cleanDistDir: true, + transpilePackages: ["@example/shared"], + output: "standalone", + // outputFileTracingRoot: "../sst", + eslint: { + ignoreDuringBuilds: true, + }, + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "opennext.js.org", + }, + ], + }, + redirects: async () => { + return [ + { + source: "/next-config-redirect-missing", + destination: "/config-redirect?missing=true", + permanent: true, + missing: [{ type: "cookie", key: "missing-cookie" }], + }, + { + source: "/next-config-redirect-not-missing", + destination: "/config-redirect?missing=true", + permanent: true, + missing: [{ type: "cookie", key: "from" }], // middleware sets this cookie + }, + { + source: "/next-config-redirect-has", + destination: "/config-redirect?has=true", + permanent: true, + has: [{ type: "cookie", key: "from" }], + }, + { + source: "/next-config-redirect-has-with-value", + destination: "/config-redirect?hasWithValue=true", + permanent: true, + has: [{ type: "cookie", key: "from", value: "middleware" }], + }, + { + source: "/next-config-redirect-has-with-bad-value", + destination: "/config-redirect?hasWithBadValue=true", + permanent: true, + has: [{ type: "cookie", key: "from", value: "wrongvalue" }], + }, + { + source: "/next-config-redirect-without-locale-support", + destination: "https://opennext.js.org/", + permanent: false, + basePath: false, + locale: false, + }, + ]; + }, + async headers() { + return [ + { + source: "/(.*)", + headers: [ + { + key: "e2e-headers", + value: "next.config.js", + }, + ], + }, + ]; + }, +}; + +export default nextConfig; diff --git a/examples/e2e/app-router/open-next.config.ts b/examples/e2e/app-router/open-next.config.ts new file mode 100644 index 00000000..0f7794ff --- /dev/null +++ b/examples/e2e/app-router/open-next.config.ts @@ -0,0 +1,25 @@ +import type { OpenNextConfig } from "@opennextjs/aws/types/open-next"; + +const config: OpenNextConfig = { + default: { + override: { + wrapper: "cloudflare-node", + converter: "edge", + // Unused implementation + incrementalCache: "dummy", + tagCache: "dummy", + queue: "dummy", + }, + }, + + middleware: { + external: true, + override: { + wrapper: "cloudflare-edge", + converter: "edge", + proxyExternalRequest: "fetch", + }, + }, +}; + +export default config; diff --git a/examples/e2e/app-router/package.json b/examples/e2e/app-router/package.json new file mode 100644 index 00000000..68eedd7a --- /dev/null +++ b/examples/e2e/app-router/package.json @@ -0,0 +1,32 @@ +{ + "name": "app-router", + "version": "0.1.4", + "private": true, + "scripts": { + "openbuild": "node ../../packages/open-next/dist/index.js build --streaming --build-command \"npx turbo build\"", + "dev": "next dev --turbopack --port 3001", + "build": "next build", + "start": "next start --port 3001", + "lint": "next lint", + "clean": "rm -rf .turbo node_modules .next .open-next", + "build:worker-tofix": "pnpm opennextjs-cloudflare", + "dev:worker": "wrangler dev --port 8770 --inspector-port 9330", + "preview": "pnpm build:worker && pnpm dev:worker" + }, + "dependencies": { + "@opennextjs/cloudflare": "workspace:*", + "@example/shared": "workspace:*", + "next": "catalog:e2e", + "react": "catalog:e2e", + "react-dom": "catalog:e2e" + }, + "devDependencies": { + "@types/node": "catalog:e2e", + "@types/react": "catalog:e2e", + "@types/react-dom": "catalog:e2e", + "autoprefixer": "catalog:e2e", + "postcss": "catalog:e2e", + "tailwindcss": "catalog:e2e", + "typescript": "catalog:default" + } +} diff --git a/examples/e2e/app-router/postcss.config.js b/examples/e2e/app-router/postcss.config.js new file mode 100644 index 00000000..12a703d9 --- /dev/null +++ b/examples/e2e/app-router/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/examples/e2e/app-router/public/favicon.ico b/examples/e2e/app-router/public/favicon.ico new file mode 100644 index 00000000..4ba005f2 Binary files /dev/null and b/examples/e2e/app-router/public/favicon.ico differ diff --git a/examples/e2e/app-router/public/static/corporate_holiday_card.jpg b/examples/e2e/app-router/public/static/corporate_holiday_card.jpg new file mode 100644 index 00000000..0df96ae2 Binary files /dev/null and b/examples/e2e/app-router/public/static/corporate_holiday_card.jpg differ diff --git a/examples/e2e/app-router/public/static/frank.webp b/examples/e2e/app-router/public/static/frank.webp new file mode 100644 index 00000000..b2cc67f0 Binary files /dev/null and b/examples/e2e/app-router/public/static/frank.webp differ diff --git a/examples/e2e/app-router/sst-env.d.ts b/examples/e2e/app-router/sst-env.d.ts new file mode 100644 index 00000000..59d33569 --- /dev/null +++ b/examples/e2e/app-router/sst-env.d.ts @@ -0,0 +1 @@ +/// <reference path="../example_sst/.sst/types/index.ts" /> diff --git a/examples/e2e/app-router/tailwind.config.ts b/examples/e2e/app-router/tailwind.config.ts new file mode 100644 index 00000000..edaaef36 --- /dev/null +++ b/examples/e2e/app-router/tailwind.config.ts @@ -0,0 +1,15 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: ["./app/**/*.{js,ts,jsx,tsx,mdx}", "../../examples/shared/**/*.{jsx,tsx}"], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/examples/e2e/app-router/tsconfig.json b/examples/e2e/app-router/tsconfig.json new file mode 100644 index 00000000..9e539bbd --- /dev/null +++ b/examples/e2e/app-router/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"], + "@example/shared": ["../shared"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules", "open-next.config.ts"] +} diff --git a/examples/e2e/app-router/wrangler.json b/examples/e2e/app-router/wrangler.json new file mode 100644 index 00000000..f8b66a4e --- /dev/null +++ b/examples/e2e/app-router/wrangler.json @@ -0,0 +1,11 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "app-router", + "compatibility_date": "2024-12-30", + "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS" + } +} diff --git a/examples/e2e/shared/api/index.ts b/examples/e2e/shared/api/index.ts new file mode 100644 index 00000000..f6f6d6d8 --- /dev/null +++ b/examples/e2e/shared/api/index.ts @@ -0,0 +1,37 @@ +"use server"; +import data from "./songs.json"; + +export type Song = (typeof data.songs)[0]; +export type Album = { album: string; artist: string; songs: Song[] }; +const albumsMap: { [key: string]: Song[] } = {}; + +const albums: Album[] = []; +data.songs.forEach((s) => { + if (!albumsMap[s.album]) { + albumsMap[s.album] = [s]; + } else { + albumsMap[s.album].push(s); + } +}); + +Object.entries(albumsMap).forEach(([key, album]) => { + albums.push({ + album: album[0].album, + artist: album[0].artist, + songs: album, + }); +}); + +export async function getAlbums() { + return albums; +} + +export async function getSongs() { + return data.songs; +} + +export async function getSong(album: string, title: string) { + return data.songs.find( + (song) => song.album === decodeURIComponent(album) && song.title === decodeURIComponent(title) + ); +} diff --git a/examples/e2e/shared/api/songs.json b/examples/e2e/shared/api/songs.json new file mode 100644 index 00000000..2f277a73 --- /dev/null +++ b/examples/e2e/shared/api/songs.json @@ -0,0 +1,36 @@ +{ + "songs": [ + { + "rank": 1, + "title": "I'm never gonna give you up", + "artist": "Rick Astley", + "album": "Hold Me In Your Arms", + "year": "1965", + "videoId": "dQw4w9WgXcQ" + }, + { + "rank": 2, + "title": "My Wang", + "artist": "Frank Wangnatra", + "album": "@franjiewang", + "year": "2023", + "videoId": "qQzdAsjWGPg" + }, + { + "rank": 3, + "title": "Excuse me miSST", + "artist": "Jay-Air", + "album": "@Jayair", + "year": "2023", + "videoId": "tnDh0JhmaFw" + }, + { + "rank": 4, + "title": "I don't want another CONSOLE-RRY", + "artist": "Dax", + "album": "@thxdr", + "year": "2023", + "videoId": "4JI70_9acgE" + } + ] +} diff --git a/examples/e2e/shared/components/Album/Album.tsx b/examples/e2e/shared/components/Album/Album.tsx new file mode 100644 index 00000000..b5eac0a1 --- /dev/null +++ b/examples/e2e/shared/components/Album/Album.tsx @@ -0,0 +1,17 @@ +import type { Album } from "../../api/index"; +import Song from "./Song"; + +type Props = { + album: Album; +}; +export default function Album({ album }: Props) { + return ( + <div className="border p-2 my-4 mx-2"> + <div>Album: {album.album}</div> + <div>Artist: {album.artist}</div> + {album.songs.map((song) => ( + <Song song={song} /> + ))} + </div> + ); +} diff --git a/examples/e2e/shared/components/Album/Song.tsx b/examples/e2e/shared/components/Album/Song.tsx new file mode 100644 index 00000000..0cc93e77 --- /dev/null +++ b/examples/e2e/shared/components/Album/Song.tsx @@ -0,0 +1,23 @@ +import type { Song } from "../../api/index"; + +type Props = { + song: Song; + play?: boolean; +}; +export default function Song({ song, play }: Props) { + return ( + <div className="border p-1 hover:bg-green-600"> + <div>Song: {song.title}</div> + <div>Year: {song.year}</div> + {play && ( + <iframe + width="560" + height="315" + title={song.title} + allowFullScreen + src={`https://youtube.com/embed/${song?.videoId}?autoplay=1`} + ></iframe> + )} + </div> + ); +} diff --git a/examples/e2e/shared/components/Album/index.tsx b/examples/e2e/shared/components/Album/index.tsx new file mode 100644 index 00000000..633e3cdc --- /dev/null +++ b/examples/e2e/shared/components/Album/index.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; + +import type { Album } from "../../api/index"; +import Song from "./Song"; + +type Props = { + album: Album; +}; +export default function Album({ album }: Props) { + return ( + <div className="border p-2 my-4 mx-2"> + <div>Album: {album.album}</div> + <div>Artist: {album.artist}</div> + {album.songs.map((song) => ( + <Link href={`/albums/${encodeURIComponent(album.album)}/${song.title}`}> + <Song song={song} /> + </Link> + ))} + </div> + ); +} diff --git a/examples/e2e/shared/components/Filler/index.tsx b/examples/e2e/shared/components/Filler/index.tsx new file mode 100644 index 00000000..60426aeb --- /dev/null +++ b/examples/e2e/shared/components/Filler/index.tsx @@ -0,0 +1,13 @@ +interface FillerProps { + // Size in kb of the filler + size: number; +} + +//This component is there to demonstrate how you could bypass streaming buffering in aws lambda. +//Hopefully, this will be fixed in the future and this component will be removed. +// https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/issues/94 +export default function Filler({ size }: FillerProps) { + const str = "a".repeat(size * 1024); + const byteSize = new TextEncoder().encode(str).length; + return <script type="application/json">{JSON.stringify({ filler: str, byteSize })}</script>; +} diff --git a/examples/e2e/shared/components/Modal/index.tsx b/examples/e2e/shared/components/Modal/index.tsx new file mode 100644 index 00000000..e522e273 --- /dev/null +++ b/examples/e2e/shared/components/Modal/index.tsx @@ -0,0 +1,51 @@ +"use client"; +import { useCallback, useEffect, useRef } from "react"; + +import { useRouter } from "next/navigation"; +import type { MouseEventHandler } from "react"; + +export default function Modal({ children }: { children: React.ReactNode }) { + const overlay = useRef(null); + const wrapper = useRef(null); + const router = useRouter(); + + const onDismiss = useCallback(() => { + router.back(); + }, [router]); + + const onClick: MouseEventHandler = useCallback( + (e) => { + if (onDismiss && (e.target === overlay.current || e.target === wrapper.current)) { + onDismiss(); + } + }, + [onDismiss, overlay, wrapper] + ); + + const onKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.key === "Escape") onDismiss(); + }, + [onDismiss] + ); + + useEffect(() => { + document.addEventListener("keydown", onKeyDown); + return () => document.removeEventListener("keydown", onKeyDown); + }, [onKeyDown]); + + return ( + <div + ref={overlay} + className="fixed z-10 left-0 right-0 top-0 bottom-0 mx-auto bg-black/80" + onClick={onClick} + > + <div + ref={wrapper} + className="absolute top-32 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full sm:w-10/12 md:w-8/12 lg:w-1/2 p-6" + > + {children} + </div> + </div> + ); +} diff --git a/examples/e2e/shared/components/Nav/index.tsx b/examples/e2e/shared/components/Nav/index.tsx new file mode 100644 index 00000000..14ae126c --- /dev/null +++ b/examples/e2e/shared/components/Nav/index.tsx @@ -0,0 +1,27 @@ +"use client"; +import Link from "next/link"; +import type { PropsWithChildren } from "react"; + +type Props = PropsWithChildren & { + href: string; + title: string; + icon?: string; +}; +export default function Nav(p: Props) { + const { children, href, title, icon = "/static/frank.webp" } = p; + return ( + <Link href={href} className="flex flex-col group border p-2 rounded-sm border-orange-500"> + <div className="flex items-center relative"> + <div>{title}</div> + <div> + <img + width={32} + className="absolute -top-2 group-hover:rotate-12 group-hover:ml-10 group-hover:scale-125 transition-all" + src={icon} + /> + </div> + </div> + <div>{children}</div> + </Link> + ); +} diff --git a/examples/e2e/shared/package.json b/examples/e2e/shared/package.json new file mode 100644 index 00000000..7ddd96bf --- /dev/null +++ b/examples/e2e/shared/package.json @@ -0,0 +1,19 @@ +{ + "name": "@example/shared", + "version": "0.0.0", + "private": true, + "scripts": { + "clean": "rm -rf .turbo && rm -rf node_modules" + }, + "dependencies": { + "react": "catalog:e2e", + "react-dom": "catalog:e2e" + }, + "devDependencies": { + "@types/react": "catalog:e2e", + "@types/react-dom": "catalog:e2e" + }, + "peerDependencies": { + "next": "catalog:e2e" + } +} diff --git a/examples/e2e/shared/sst-env.d.ts b/examples/e2e/shared/sst-env.d.ts new file mode 100644 index 00000000..59d33569 --- /dev/null +++ b/examples/e2e/shared/sst-env.d.ts @@ -0,0 +1 @@ +/// <reference path="../example_sst/.sst/types/index.ts" /> diff --git a/examples/e2e/shared/tsconfig.json b/examples/e2e/shared/tsconfig.json new file mode 100644 index 00000000..aa168247 --- /dev/null +++ b/examples/e2e/shared/tsconfig.json @@ -0,0 +1,32 @@ +{ + "include": ["."], + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "composite": false, + "declaration": false, + "declarationMap": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "NodeNext", + "noUnusedLocals": true, + "noUnusedParameters": true, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true, + "removeComments": true, + "resolveJsonModule": true, + "sourceMap": true, + "baseUrl": ".", + "jsx": "react-jsx", + "lib": ["ES2022", "DOM"], + "module": "NodeNext", + "target": "ESNext", + "paths": { + "~/*": ["./*"] + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdb1b7b9..6034fd57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,8 +76,8 @@ catalogs: specifier: ^4.19.2 version: 4.19.2 typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.7.3 + version: 5.7.3 typescript-eslint: specifier: ^8.7.0 version: 8.7.0 @@ -87,6 +87,34 @@ catalogs: wrangler: specifier: ^3.101.0 version: 3.101.0 + e2e: + '@types/node': + specifier: 20.17.6 + version: 20.17.6 + '@types/react': + specifier: 19.0.0 + version: 19.0.0 + '@types/react-dom': + specifier: 19.0.0 + version: 19.0.0 + autoprefixer: + specifier: 10.4.15 + version: 10.4.15 + next: + specifier: 15.1.0 + version: 15.1.0 + postcss: + specifier: 8.4.27 + version: 8.4.27 + react: + specifier: 19.0.0 + version: 19.0.0 + react-dom: + specifier: 19.0.0 + version: 19.0.0 + tailwindcss: + specifier: 3.3.3 + version: 3.3.3 importers: @@ -177,7 +205,7 @@ importers: version: 8.57.1 eslint-config-next: specifier: 14.2.14 - version: 14.2.14(eslint@8.57.1)(typescript@5.5.4) + version: 14.2.14(eslint@8.57.1)(typescript@5.7.3) postcss: specifier: ^8 version: 8.4.31 @@ -186,11 +214,70 @@ importers: version: 3.4.11 typescript: specifier: 'catalog:' - version: 5.5.4 + version: 5.7.3 wrangler: specifier: 'catalog:' version: 3.101.0(@cloudflare/workers-types@4.20250109.0) + examples/e2e/app-router: + dependencies: + '@example/shared': + specifier: workspace:* + version: link:../shared + '@opennextjs/cloudflare': + specifier: workspace:* + version: link:../../../packages/cloudflare + next: + specifier: catalog:e2e + version: 15.1.0(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: + specifier: catalog:e2e + version: 19.0.0 + react-dom: + specifier: catalog:e2e + version: 19.0.0(react@19.0.0) + devDependencies: + '@types/node': + specifier: catalog:e2e + version: 20.17.6 + '@types/react': + specifier: catalog:e2e + version: 19.0.0 + '@types/react-dom': + specifier: catalog:e2e + version: 19.0.0 + autoprefixer: + specifier: catalog:e2e + version: 10.4.15(postcss@8.4.27) + postcss: + specifier: catalog:e2e + version: 8.4.27 + tailwindcss: + specifier: catalog:e2e + version: 3.3.3 + typescript: + specifier: catalog:default + version: 5.7.3 + + examples/e2e/shared: + dependencies: + next: + specifier: catalog:e2e + version: 15.1.0(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: + specifier: catalog:e2e + version: 19.0.0 + react-dom: + specifier: catalog:e2e + version: 19.0.0(react@19.0.0) + devDependencies: + '@types/react': + specifier: catalog:e2e + version: 19.0.0 + '@types/react-dom': + specifier: catalog:e2e + version: 19.0.0 + examples/middleware: dependencies: '@clerk/nextjs': @@ -226,7 +313,7 @@ importers: version: 9.11.1(jiti@1.21.6) typescript: specifier: 'catalog:' - version: 5.5.4 + version: 5.7.3 wrangler: specifier: 'catalog:' version: 3.101.0(@cloudflare/workers-types@4.20250109.0) @@ -281,7 +368,7 @@ importers: version: 3.4.11 typescript: specifier: 'catalog:' - version: 5.5.4 + version: 5.7.3 wrangler: specifier: 'catalog:' version: 3.101.0(@cloudflare/workers-types@4.20250109.0) @@ -348,7 +435,7 @@ importers: version: 3.4.11 typescript: specifier: 'catalog:' - version: 5.5.4 + version: 5.7.3 wrangler: specifier: 'catalog:' version: 3.101.0(@cloudflare/workers-types@4.20250109.0) @@ -397,7 +484,7 @@ importers: version: 9.11.1(jiti@1.21.6) eslint-plugin-import: specifier: 'catalog:' - version: 2.31.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.11.1(jiti@1.21.6)) + version: 2.31.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint@9.11.1(jiti@1.21.6)) eslint-plugin-simple-import-sort: specifier: 'catalog:' version: 12.1.1(eslint@9.11.1(jiti@1.21.6)) @@ -412,16 +499,16 @@ importers: version: 5.4.1 next: specifier: 'catalog:' - version: 14.2.11(@playwright/test@1.47.0)(react-dom@19.0.0-rc-3208e73e-20240730(react@19.0.0-rc-3208e73e-20240730))(react@19.0.0-rc-3208e73e-20240730) + version: 14.2.11(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) rimraf: specifier: 'catalog:' version: 6.0.1 typescript: specifier: 'catalog:' - version: 5.5.4 + version: 5.7.3 typescript-eslint: specifier: 'catalog:' - version: 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + version: 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) vitest: specifier: 'catalog:' version: 2.1.1(@types/node@22.2.0)(terser@5.16.9) @@ -1787,6 +1874,9 @@ packages: '@next/env@15.0.0-canary.113': resolution: {integrity: sha512-hiD7ux+YPCUJi3up0dHnROYBYg/AuPErOcBBzjCkKQ1q4ufuUNBQms4oDeOiHG9+Qga8mN5+k2L5qm7rNhzU4g==} + '@next/env@15.1.0': + resolution: {integrity: sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==} + '@next/eslint-plugin-next@14.2.14': resolution: {integrity: sha512-kV+OsZ56xhj0rnTn6HegyTGkoa16Mxjrpk7pjWumyB2P8JVQb8S9qtkjy/ye0GnTr4JWtWG4x/2qN40lKZ3iVQ==} @@ -1802,6 +1892,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.1.0': + resolution: {integrity: sha512-ZU8d7xxpX14uIaFC3nsr4L++5ZS/AkWDm1PzPO6gD9xWhFkOj2hzSbSIxoncsnlJXB1CbLOfGVN4Zk9tg83PUw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@14.2.11': resolution: {integrity: sha512-lnB0zYCld4yE0IX3ANrVMmtAbziBb7MYekcmR6iE9bujmgERl6+FK+b0MBq0pl304lYe7zO4yxJus9H/Af8jbg==} engines: {node: '>= 10'} @@ -1814,6 +1910,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.1.0': + resolution: {integrity: sha512-DQ3RiUoW2XC9FcSM4ffpfndq1EsLV0fj0/UY33i7eklW5akPUCo6OX2qkcLXZ3jyPdo4sf2flwAED3AAq3Om2Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@14.2.11': resolution: {integrity: sha512-Ulo9TZVocYmUAtzvZ7FfldtwUoQY0+9z3BiXZCLSUwU2bp7GqHA7/bqrfsArDlUb2xeGwn3ZuBbKtNK8TR0A8w==} engines: {node: '>= 10'} @@ -1826,6 +1928,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.1.0': + resolution: {integrity: sha512-M+vhTovRS2F//LMx9KtxbkWk627l5Q7AqXWWWrfIzNIaUFiz2/NkOFkxCFyNyGACi5YbA8aekzCLtbDyfF/v5Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@14.2.11': resolution: {integrity: sha512-fH377DnKGyUnkWlmUpFF1T90m0dADBfK11dF8sOQkiELF9M+YwDRCGe8ZyDzvQcUd20Rr5U7vpZRrAxKwd3Rzg==} engines: {node: '>= 10'} @@ -1838,6 +1946,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.1.0': + resolution: {integrity: sha512-Qn6vOuwaTCx3pNwygpSGtdIu0TfS1KiaYLYXLH5zq1scoTXdwYfdZtwvJTpB1WrLgiQE2Ne2kt8MZok3HlFqmg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@14.2.11': resolution: {integrity: sha512-a0TH4ZZp4NS0LgXP/488kgvWelNpwfgGTUCDXVhPGH6pInb7yIYNgM4kmNWOxBFt+TIuOH6Pi9NnGG4XWFUyXQ==} engines: {node: '>= 10'} @@ -1850,6 +1964,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.1.0': + resolution: {integrity: sha512-yeNh9ofMqzOZ5yTOk+2rwncBzucc6a1lyqtg8xZv0rH5znyjxHOWsoUtSq4cUTeeBIiXXX51QOOe+VoCjdXJRw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@14.2.11': resolution: {integrity: sha512-DYYZcO4Uir2gZxA4D2JcOAKVs8ZxbOFYPpXSVIgeoQbREbeEHxysVsg3nY4FrQy51e5opxt5mOHl/LzIyZBoKA==} engines: {node: '>= 10'} @@ -1862,6 +1982,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.1.0': + resolution: {integrity: sha512-t9IfNkHQs/uKgPoyEtU912MG6a1j7Had37cSUyLTKx9MnUpjj+ZDKw9OyqTI9OwIIv0wmkr1pkZy+3T5pxhJPg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@14.2.11': resolution: {integrity: sha512-PwqHeKG3/kKfPpM6of1B9UJ+Er6ySUy59PeFu0Un0LBzJTRKKAg2V6J60Yqzp99m55mLa+YTbU6xj61ImTv9mg==} engines: {node: '>= 10'} @@ -1874,6 +2000,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.1.0': + resolution: {integrity: sha512-WEAoHyG14t5sTavZa1c6BnOIEukll9iqFRTavqRVPfYmfegOAd5MaZfXgOGG6kGo1RduyGdTHD4+YZQSdsNZXg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-ia32-msvc@14.2.11': resolution: {integrity: sha512-0U7PWMnOYIvM74GY6rbH6w7v+vNPDVH1gUhlwHpfInJnNe5LkmUZqhp7FNWeNa5wbVgRcRi1F1cyxp4dmeLLvA==} engines: {node: '>= 10'} @@ -1898,6 +2030,12 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.1.0': + resolution: {integrity: sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@noble/ciphers@1.1.3': resolution: {integrity: sha512-Ygv6WnWJHLLiW4fnNDC1z+i13bud+enXOFRBlpxI+NJliPWx5wdR+oWlTjLuBPTqjUjtHXtjkU6w3kuuH6upZA==} engines: {node: ^14.21.3 || >=16} @@ -2665,6 +2803,9 @@ packages: '@swc/helpers@0.5.12': resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/helpers@0.5.5': resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} @@ -2729,6 +2870,9 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@20.17.6': + resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==} + '@types/node@22.2.0': resolution: {integrity: sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==} @@ -2741,9 +2885,15 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-dom@19.0.0': + resolution: {integrity: sha512-1KfiQKsH1o00p9m5ag12axHQSb3FOU9H20UTrujVSkNhuCrRHiQWFqgEnTNK5ZNfnzZv8UWrnXVqCmCF9fgY3w==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/react@19.0.0': + resolution: {integrity: sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==} + '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -2961,6 +3111,13 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + autoprefixer@10.4.15: + resolution: {integrity: sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + autoprefixer@10.4.20: resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} @@ -4610,6 +4767,27 @@ packages: sass: optional: true + next@15.1.0: + resolution: {integrity: sha512-QKhzt6Y8rgLNlj30izdMbxAwjHMFANnLwDwZ+WQh5sMhyt4lEBqDK9QpvWHtIM4rINKPoJ8aiRZKg5ULSybVHw==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -4906,6 +5084,10 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.27: + resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -5039,6 +5221,11 @@ packages: peerDependencies: react: ^18.3.1 + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + react-dom@19.0.0-rc-3208e73e-20240730: resolution: {integrity: sha512-jspKji5vQTTlFY7zFGh0VB+rZV+5FweCQkYxtLoPZvc5ZH6vEf1n8d+4h9YqNqF8nnhFkcHO8XLtKTtTyVGSXA==} peerDependencies: @@ -5051,6 +5238,10 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + react@19.0.0-rc-3208e73e-20240730: resolution: {integrity: sha512-4TmFOcgSfwM8w18vXLnEt8tb3ilO9a0GRJA9zQSYjZ5ie6g/zkxagRvZvZbEmhaNgDSF/PKmEdWmfBtlUBcjkA==} engines: {node: '>=0.10.0'} @@ -5185,6 +5376,9 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + scheduler@0.25.0-rc-3208e73e-20240730: resolution: {integrity: sha512-eCGy6Bm6PX2JB7dLumQuEYwmXku9HweMfdXCQQ2ZaRG3kwxK76RWFr7CsW/LHju8fiDSiJajl0Iq62uoaH9VLQ==} @@ -5480,6 +5674,11 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwindcss@3.3.3: + resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} + engines: {node: '>=14.0.0'} + hasBin: true + tailwindcss@3.4.11: resolution: {integrity: sha512-qhEuBcLemjSJk5ajccN9xJFtM/h0AVCPaA6C92jNP+M2J8kX+eMJHI7R2HFKUvvAsMpcfLILMCFYSeDwpMmlUg==} engines: {node: '>=14.0.0'} @@ -5572,6 +5771,9 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.19.2: resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'} @@ -5634,8 +5836,8 @@ packages: typescript: optional: true - typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true @@ -5648,6 +5850,9 @@ packages: undici-types@6.13.0: resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} @@ -8035,6 +8240,8 @@ snapshots: '@next/env@15.0.0-canary.113': {} + '@next/env@15.1.0': {} + '@next/eslint-plugin-next@14.2.14': dependencies: glob: 10.3.10 @@ -8045,42 +8252,63 @@ snapshots: '@next/swc-darwin-arm64@15.0.0-canary.113': optional: true + '@next/swc-darwin-arm64@15.1.0': + optional: true + '@next/swc-darwin-x64@14.2.11': optional: true '@next/swc-darwin-x64@15.0.0-canary.113': optional: true + '@next/swc-darwin-x64@15.1.0': + optional: true + '@next/swc-linux-arm64-gnu@14.2.11': optional: true '@next/swc-linux-arm64-gnu@15.0.0-canary.113': optional: true + '@next/swc-linux-arm64-gnu@15.1.0': + optional: true + '@next/swc-linux-arm64-musl@14.2.11': optional: true '@next/swc-linux-arm64-musl@15.0.0-canary.113': optional: true + '@next/swc-linux-arm64-musl@15.1.0': + optional: true + '@next/swc-linux-x64-gnu@14.2.11': optional: true '@next/swc-linux-x64-gnu@15.0.0-canary.113': optional: true + '@next/swc-linux-x64-gnu@15.1.0': + optional: true + '@next/swc-linux-x64-musl@14.2.11': optional: true '@next/swc-linux-x64-musl@15.0.0-canary.113': optional: true + '@next/swc-linux-x64-musl@15.1.0': + optional: true + '@next/swc-win32-arm64-msvc@14.2.11': optional: true '@next/swc-win32-arm64-msvc@15.0.0-canary.113': optional: true + '@next/swc-win32-arm64-msvc@15.1.0': + optional: true + '@next/swc-win32-ia32-msvc@14.2.11': optional: true @@ -8093,6 +8321,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.0.0-canary.113': optional: true + '@next/swc-win32-x64-msvc@15.1.0': + optional: true + '@noble/ciphers@1.1.3': {} '@noble/curves@1.7.0': @@ -9187,6 +9418,10 @@ snapshots: dependencies: tslib: 2.6.3 + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + '@swc/helpers@0.5.5': dependencies: '@swc/counter': 0.1.3 @@ -9255,6 +9490,10 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@20.17.6': + dependencies: + undici-types: 6.19.8 + '@types/node@22.2.0': dependencies: undici-types: 6.13.0 @@ -9267,76 +9506,84 @@ snapshots: dependencies: '@types/react': 18.3.3 + '@types/react-dom@19.0.0': + dependencies: + '@types/react': 19.0.0 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/react@19.0.0': + dependencies: + csstype: 3.1.3 + '@types/resolve@1.20.6': {} '@types/unist@3.0.3': {} '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.7.0 - '@typescript-eslint/type-utils': 8.7.0(eslint@8.57.1)(typescript@5.5.4) - '@typescript-eslint/utils': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.7.0(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/utils': 8.7.0(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.7.0 eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.3.0(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.7.0 - '@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) + '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.7.0 eslint: 9.11.1(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.3.0(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4)': + '@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.7.0 '@typescript-eslint/types': 8.7.0 - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.7.0 debug: 4.3.6 eslint: 8.57.1 optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.7.0 '@typescript-eslint/types': 8.7.0 - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.7.0 debug: 4.3.6 eslint: 9.11.1(jiti@1.21.6) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -9345,33 +9592,33 @@ snapshots: '@typescript-eslint/types': 8.7.0 '@typescript-eslint/visitor-keys': 8.7.0 - '@typescript-eslint/type-utils@8.7.0(eslint@8.57.1)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.7.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.7.0(eslint@8.57.1)(typescript@5.7.3) debug: 4.3.6 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.3.0(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - eslint - supports-color - '@typescript-eslint/type-utils@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) debug: 4.3.6 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.3.0(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - eslint - supports-color '@typescript-eslint/types@8.7.0': {} - '@typescript-eslint/typescript-estree@8.7.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.7.0(typescript@5.7.3)': dependencies: '@typescript-eslint/types': 8.7.0 '@typescript-eslint/visitor-keys': 8.7.0 @@ -9380,29 +9627,29 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.5.4) + ts-api-utils: 1.3.0(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.7.0(eslint@8.57.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.7.0(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@typescript-eslint/scope-manager': 8.7.0 '@typescript-eslint/types': 8.7.0 - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) eslint: 8.57.1 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/utils@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@9.11.1(jiti@1.21.6)) '@typescript-eslint/scope-manager': 8.7.0 '@typescript-eslint/types': 8.7.0 - '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.7.0(typescript@5.7.3) eslint: 9.11.1(jiti@1.21.6) transitivePeerDependencies: - supports-color @@ -9595,6 +9842,16 @@ snapshots: ast-types-flow@0.0.8: {} + autoprefixer@10.4.15(postcss@8.4.27): + dependencies: + browserslist: 4.24.0 + caniuse-lite: 1.0.30001664 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.0 + postcss: 8.4.27 + postcss-value-parser: 4.2.0 + autoprefixer@10.4.20(postcss@8.4.47): dependencies: browserslist: 4.24.0 @@ -10229,21 +10486,21 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@14.2.14(eslint@8.57.1)(typescript@5.5.4): + eslint-config-next@14.2.14(eslint@8.57.1)(typescript@5.7.3): dependencies: '@next/eslint-plugin-next': 14.2.14 '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) - '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.7.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1) eslint-plugin-react: 7.36.1(eslint@8.57.1) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - eslint-import-resolver-webpack - eslint-plugin-import-x @@ -10257,47 +10514,47 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.7.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@1.21.6)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) eslint: 9.11.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -10308,7 +10565,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -10319,13 +10576,13 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.11.1(jiti@1.21.6)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint@9.11.1(jiti@1.21.6)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -10336,7 +10593,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.11.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@1.21.6)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@1.21.6)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -10348,7 +10605,7 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -11600,7 +11857,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@14.2.11(@playwright/test@1.47.0)(react-dom@19.0.0-rc-3208e73e-20240730(react@19.0.0-rc-3208e73e-20240730))(react@19.0.0-rc-3208e73e-20240730): + next@14.2.11(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@next/env': 14.2.11 '@swc/helpers': 0.5.5 @@ -11608,9 +11865,9 @@ snapshots: caniuse-lite: 1.0.30001664 graceful-fs: 4.2.11 postcss: 8.4.31 - react: 19.0.0-rc-3208e73e-20240730 - react-dom: 19.0.0-rc-3208e73e-20240730(react@19.0.0-rc-3208e73e-20240730) - styled-jsx: 5.1.1(react@19.0.0-rc-3208e73e-20240730) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.1(react@19.0.0) optionalDependencies: '@next/swc-darwin-arm64': 14.2.11 '@next/swc-darwin-x64': 14.2.11 @@ -11654,6 +11911,32 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.1.0(@playwright/test@1.47.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@next/env': 15.1.0 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001664 + postcss: 8.4.31 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.1.0 + '@next/swc-darwin-x64': 15.1.0 + '@next/swc-linux-arm64-gnu': 15.1.0 + '@next/swc-linux-arm64-musl': 15.1.0 + '@next/swc-linux-x64-gnu': 15.1.0 + '@next/swc-linux-x64-musl': 15.1.0 + '@next/swc-win32-arm64-msvc': 15.1.0 + '@next/swc-win32-x64-msvc': 15.1.0 + '@playwright/test': 1.47.0 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -11894,28 +12177,28 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-import@15.1.0(postcss@8.4.31): + postcss-import@15.1.0(postcss@8.4.47): dependencies: - postcss: 8.4.31 + postcss: 8.4.47 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.4.31): + postcss-js@4.0.1(postcss@8.4.47): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.31 + postcss: 8.4.47 - postcss-load-config@4.0.2(postcss@8.4.31): + postcss-load-config@4.0.2(postcss@8.4.47): dependencies: lilconfig: 3.1.2 yaml: 2.5.1 optionalDependencies: - postcss: 8.4.31 + postcss: 8.4.47 - postcss-nested@6.2.0(postcss@8.4.31): + postcss-nested@6.2.0(postcss@8.4.47): dependencies: - postcss: 8.4.31 + postcss: 8.4.47 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -11930,6 +12213,12 @@ snapshots: postcss-value-parser@4.2.0: {} + postcss@8.4.27: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + postcss@8.4.31: dependencies: nanoid: 3.3.7 @@ -12011,6 +12300,11 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + react-dom@19.0.0-rc-3208e73e-20240730(react@19.0.0-rc-3208e73e-20240730): dependencies: react: 19.0.0-rc-3208e73e-20240730 @@ -12022,6 +12316,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + react@19.0.0: {} + react@19.0.0-rc-3208e73e-20240730: {} read-cache@1.0.0: @@ -12216,6 +12512,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + scheduler@0.25.0: {} + scheduler@0.25.0-rc-3208e73e-20240730: {} section-matter@1.0.0: @@ -12510,10 +12808,15 @@ snapshots: client-only: 0.0.1 react: 18.3.1 - styled-jsx@5.1.1(react@19.0.0-rc-3208e73e-20240730): + styled-jsx@5.1.1(react@19.0.0): dependencies: client-only: 0.0.1 - react: 19.0.0-rc-3208e73e-20240730 + react: 19.0.0 + + styled-jsx@5.1.6(react@19.0.0): + dependencies: + client-only: 0.0.1 + react: 19.0.0 styled-jsx@5.1.6(react@19.0.0-rc-3208e73e-20240730): dependencies: @@ -12548,6 +12851,33 @@ snapshots: tabbable@6.2.0: {} + tailwindcss@3.3.3: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + tailwindcss@3.4.11: dependencies: '@alloc/quick-lru': 5.2.0 @@ -12564,11 +12894,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.1 - postcss: 8.4.31 - postcss-import: 15.1.0(postcss@8.4.31) - postcss-js: 4.0.1(postcss@8.4.31) - postcss-load-config: 4.0.2(postcss@8.4.31) - postcss-nested: 6.2.0(postcss@8.4.31) + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47) + postcss-nested: 6.2.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 resolve: 1.22.8 sucrase: 3.35.0 @@ -12625,9 +12955,9 @@ snapshots: trough@2.2.0: {} - ts-api-utils@1.3.0(typescript@5.5.4): + ts-api-utils@1.3.0(typescript@5.7.3): dependencies: - typescript: 5.5.4 + typescript: 5.7.3 ts-interface-checker@0.1.13: {} @@ -12649,6 +12979,8 @@ snapshots: tslib@2.6.3: {} + tslib@2.8.1: {} + tsx@4.19.2: dependencies: esbuild: 0.23.1 @@ -12713,18 +13045,18 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript-eslint@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4): + typescript-eslint@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) + '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) + '@typescript-eslint/utils': 8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.7.3 transitivePeerDependencies: - eslint - supports-color - typescript@5.5.4: {} + typescript@5.7.3: {} ufo@1.5.4: {} @@ -12737,6 +13069,8 @@ snapshots: undici-types@6.13.0: {} + undici-types@6.19.8: {} + undici@5.28.4: dependencies: '@fastify/busboy': 2.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a42304d3..46b43eb2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,35 +1,49 @@ packages: - - "packages/*" - - "examples/*" - - "benchmarking" + - packages/* + - examples/* + - examples/e2e/* + - benchmarking catalog: "@cloudflare/workers-types": ^4.20241230.0 "@dotenvx/dotenvx": 1.31.0 "@eslint/js": ^9.11.1 "@playwright/test": 1.47.0 - "@tsconfig/strictest": "^2.0.5" + "@tsconfig/strictest": ^2.0.5 "@types/mock-fs": ^4.13.4 "@types/node": ^22.2.0 "@types/react-dom": ^18 "@types/react": ^18 - "esbuild": ^0.23.0 - "eslint-plugin-import": "^2.31.0" - "eslint-plugin-simple-import-sort": "^12.1.1" - "eslint-plugin-unicorn": ^55.0.0 - "eslint": ^9.11.1 - "glob": ^11.0.0 - "globals": ^15.9.0 - "mock-fs": ^5.4.1 - "next": 14.2.11 - "package-manager-detector": ^0.2.0 - "react-dom": ^18 - "react": ^18 - "rimraf": "^6.0.1" - "ts-morph": ^23.0.0 - "tsup": ^8.2.4 - "tsx": ^4.19.2 - "typescript-eslint": ^8.7.0 - "typescript": ^5.5.4 - "vitest": ^2.1.1 - "wrangler": ^3.101.0 + esbuild: ^0.23.0 + eslint-plugin-import: ^2.31.0 + eslint-plugin-simple-import-sort: ^12.1.1 + eslint-plugin-unicorn: ^55.0.0 + eslint: ^9.11.1 + glob: ^11.0.0 + globals: ^15.9.0 + mock-fs: ^5.4.1 + next: 14.2.11 + package-manager-detector: ^0.2.0 + react-dom: ^18 + react: ^18 + rimraf: ^6.0.1 + ts-morph: ^23.0.0 + tsup: ^8.2.4 + tsx: ^4.19.2 + typescript-eslint: ^8.7.0 + typescript: ^5.7.3 + vitest: ^2.1.1 + wrangler: ^3.101.0 + +# e2e tests +catalogs: + e2e: + "@types/node": 20.17.6 + "@types/react-dom": 19.0.0 + "@types/react": 19.0.0 + autoprefixer: 10.4.15 + next: 15.1.0 + postcss: 8.4.27 + react-dom: 19.0.0 + react: 19.0.0 + tailwindcss: 3.3.3