diff --git a/packages/server/package.json b/packages/server/package.json index 59145c55d..60ae97483 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -66,14 +66,20 @@ "@zenstackhq/typescript-config": "workspace:*", "@zenstackhq/vitest-config": "workspace:*", "body-parser": "^2.2.0", - "supertest": "^7.1.4" + "supertest": "^7.1.4", + "express": "^5.0.0", + "next": "^15.0.0" }, "peerDependencies": { - "express": "^5.0.0" + "express": "^5.0.0", + "next": "^15.0.0" }, "peerDependenciesMeta": { "express": { "optional": true + }, + "next": { + "optional": true } } } diff --git a/packages/server/src/express/index.ts b/packages/server/src/adapter/express/index.ts similarity index 100% rename from packages/server/src/express/index.ts rename to packages/server/src/adapter/express/index.ts diff --git a/packages/server/src/express/middleware.ts b/packages/server/src/adapter/express/middleware.ts similarity index 96% rename from packages/server/src/express/middleware.ts rename to packages/server/src/adapter/express/middleware.ts index 43ccf88fd..f6db6b7e8 100644 --- a/packages/server/src/express/middleware.ts +++ b/packages/server/src/adapter/express/middleware.ts @@ -1,12 +1,15 @@ import type { ClientContract } from '@zenstackhq/orm'; import type { SchemaDef } from '@zenstackhq/orm/schema'; import type { Handler, Request, Response } from 'express'; -import type { ApiHandler } from '../types'; +import type { ApiHandler } from '../../types'; /** * Express middleware options */ export interface MiddlewareOptions { + /** + * The API handler to process requests + */ apiHandler: ApiHandler; /** diff --git a/packages/server/src/adapter/next/app-route-handler.ts b/packages/server/src/adapter/next/app-route-handler.ts new file mode 100644 index 000000000..c220e9883 --- /dev/null +++ b/packages/server/src/adapter/next/app-route-handler.ts @@ -0,0 +1,64 @@ +import type { SchemaDef } from '@zenstackhq/orm/schema'; +import { NextRequest, NextResponse } from 'next/server'; +import type { AppRouteRequestHandlerOptions } from '.'; + +type Context = { params: Promise<{ path: string[] }> }; + +/** + * Creates a Next.js "app router" API route request handler that handles ZenStack CRUD requests. + * + * @param options Options for initialization + * @returns An API route request handler + */ +export default function factory( + options: AppRouteRequestHandlerOptions, +): (req: NextRequest, context: Context) => Promise { + return async (req: NextRequest, context: Context) => { + const client = await options.getClient(req); + if (!client) { + return NextResponse.json({ message: 'unable to get ZenStackClient from request context' }, { status: 500 }); + } + + let params: Awaited; + const url = new URL(req.url); + const query = Object.fromEntries(url.searchParams); + + try { + params = await context.params; + } catch { + return NextResponse.json({ message: 'Failed to resolve request parameters' }, { status: 500 }); + } + + if (!params.path) { + return NextResponse.json( + { message: 'missing path parameter' }, + { + status: 400, + }, + ); + } + const path = params.path.join('/'); + + let requestBody: unknown; + if (req.body) { + try { + requestBody = await req.json(); + } catch { + // noop + } + } + + try { + const r = await options.apiHandler.handleRequest({ + method: req.method!, + path, + query, + requestBody, + client, + }); + return NextResponse.json(r.body, { status: r.status }); + } catch (err) { + return NextResponse.json({ message: `An unhandled error occurred: ${err}` }, { status: 500 }); + } + }; +} diff --git a/packages/server/src/adapter/next/index.ts b/packages/server/src/adapter/next/index.ts new file mode 100644 index 000000000..24f6e606d --- /dev/null +++ b/packages/server/src/adapter/next/index.ts @@ -0,0 +1,63 @@ +import type { ClientContract } from '@zenstackhq/orm'; +import type { SchemaDef } from '@zenstackhq/orm/schema'; +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { NextRequest } from 'next/server'; +import type { ApiHandler } from '../../types'; +import { default as AppRouteHandler } from './app-route-handler'; +import { default as PagesRouteHandler } from './pages-route-handler'; + +interface CommonAdapterOptions { + /** + * The API handler to process requests + */ + apiHandler: ApiHandler; +} + +/** + * Options for initializing a Next.js API endpoint request handler. + */ +export interface PageRouteRequestHandlerOptions extends CommonAdapterOptions { + /** + * Callback for getting a ZenStackClient for the given request + */ + getClient: (req: NextApiRequest, res: NextApiResponse) => ClientContract | Promise>; + + /** + * Use app dir or not + */ + useAppDir?: false | undefined; +} + +/** + * Options for initializing a Next.js 13 app dir API route handler. + */ +export interface AppRouteRequestHandlerOptions extends CommonAdapterOptions { + /** + * Callback for getting a ZenStackClient for the given request. + */ + getClient: (req: NextRequest) => ClientContract | Promise>; + + /** + * Use app dir or not + */ + useAppDir: true; +} + +/** + * Creates a Next.js API route handler. + */ +export function NextRequestHandler( + options: PageRouteRequestHandlerOptions, +): ReturnType; +export function NextRequestHandler( + options: AppRouteRequestHandlerOptions, +): ReturnType; +export function NextRequestHandler( + options: PageRouteRequestHandlerOptions | AppRouteRequestHandlerOptions, +) { + if (options.useAppDir === true) { + return AppRouteHandler(options); + } else { + return PagesRouteHandler(options); + } +} diff --git a/packages/server/src/adapter/next/pages-route-handler.ts b/packages/server/src/adapter/next/pages-route-handler.ts new file mode 100644 index 000000000..e65c65e86 --- /dev/null +++ b/packages/server/src/adapter/next/pages-route-handler.ts @@ -0,0 +1,40 @@ +import type { SchemaDef } from '@zenstackhq/orm/schema'; +import type { NextApiRequest, NextApiResponse } from 'next'; +import type { PageRouteRequestHandlerOptions } from '.'; + +/** + * Creates a Next.js API endpoint "pages" router request handler that handles ZenStack CRUD requests. + * + * @param options Options for initialization + * @returns An API endpoint request handler + */ +export default function factory( + options: PageRouteRequestHandlerOptions, +): (req: NextApiRequest, res: NextApiResponse) => Promise { + return async (req: NextApiRequest, res: NextApiResponse) => { + const client = await options.getClient(req, res); + if (!client) { + res.status(500).json({ message: 'unable to get ZenStackClient from request context' }); + return; + } + + if (!req.query['path']) { + res.status(400).json({ message: 'missing path parameter' }); + return; + } + const path = (req.query['path'] as string[]).join('/'); + + try { + const r = await options.apiHandler.handleRequest({ + method: req.method!, + path, + query: req.query as Record, + requestBody: req.body, + client, + }); + res.status(r.status).send(r.body); + } catch (err) { + res.status(500).send({ message: `An unhandled error occurred: ${err}` }); + } + }; +} diff --git a/packages/server/test/adapter/express.test.ts b/packages/server/test/adapter/express.test.ts index 7926754d6..314288caa 100644 --- a/packages/server/test/adapter/express.test.ts +++ b/packages/server/test/adapter/express.test.ts @@ -3,8 +3,8 @@ import bodyParser from 'body-parser'; import express from 'express'; import request from 'supertest'; import { describe, expect, it } from 'vitest'; +import { ZenStackMiddleware } from '../../src/adapter/express'; import { RPCApiHandler } from '../../src/api'; -import { ZenStackMiddleware } from '../../src/express'; import { makeUrl, schema } from '../utils'; describe('Express adapter tests - rpc handler', () => { diff --git a/packages/server/test/adapter/next.test.ts b/packages/server/test/adapter/next.test.ts new file mode 100644 index 000000000..def6a1d3c --- /dev/null +++ b/packages/server/test/adapter/next.test.ts @@ -0,0 +1,298 @@ +import { SchemaDef } from '@zenstackhq/orm/schema'; +import { createPolicyTestClient, createTestClient } from '@zenstackhq/testtools'; +import { createServer, RequestListener } from 'http'; +import { apiResolver } from 'next/dist/server/api-utils/node/api-resolver'; +import request from 'supertest'; +import { describe, expect, it } from 'vitest'; +import { NextRequestHandler, type PageRouteRequestHandlerOptions } from '../../src/adapter/next'; +import { RPCApiHandler } from '../../src/api'; + +function makeTestClient( + apiPath: string, + options: PageRouteRequestHandlerOptions, + qArg?: unknown, + otherArgs?: any, +) { + const pathParts = apiPath.split('/').filter((p) => p); + + const query = { + path: pathParts, + ...(qArg ? { q: JSON.stringify(qArg) } : {}), + ...otherArgs, + }; + + const handler = NextRequestHandler(options); + + const listener: RequestListener = (req, res) => { + return apiResolver( + req, + res, + query, + handler, + { + dev: false, + previewModeEncryptionKey: '', + previewModeId: '', + previewModeSigningKey: '', + }, + false, + ); + }; + + return request(createServer(listener)); +} + +describe('Next.js adapter tests - rpc handler', () => { + it('simple crud', async () => { + const model = ` +model M { + id String @id @default(cuid()) + value Int +} + `; + + const client = await createTestClient(model); + + const makeClientOptions = { + getClient: () => client, + apiHandler: new RPCApiHandler({ + schema: client.$schema, + }), + }; + + await makeTestClient('/m/create', makeClientOptions) + .post('/') + .send({ data: { id: '1', value: 1 } }) + .expect(201) + .expect((resp) => { + expect(resp.body.data.value).toBe(1); + }); + + await makeTestClient('/m/findUnique', makeClientOptions, { where: { id: '1' } }) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data.value).toBe(1); + }); + + await makeTestClient('/m/findFirst', makeClientOptions, { where: { id: '1' } }) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data.value).toBe(1); + }); + + await makeTestClient('/m/findMany', makeClientOptions, {}) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data).toHaveLength(1); + }); + + await makeTestClient('/m/update', makeClientOptions) + .put('/') + .send({ where: { id: '1' }, data: { value: 2 } }) + .expect(200) + .expect((resp) => { + expect(resp.body.data.value).toBe(2); + }); + + await makeTestClient('/m/updateMany', makeClientOptions) + .put('/') + .send({ data: { value: 4 } }) + .expect(200) + .expect((resp) => { + expect(resp.body.data.count).toBe(1); + }); + + await makeTestClient('/m/upsert', makeClientOptions) + .post('/') + .send({ where: { id: '2' }, create: { id: '2', value: 2 }, update: { value: 3 } }) + .expect(201) + .expect((resp) => { + expect(resp.body.data.value).toBe(2); + }); + + await makeTestClient('/m/upsert', makeClientOptions) + .post('/') + .send({ where: { id: '2' }, create: { id: '2', value: 2 }, update: { value: 3 } }) + .expect(201) + .expect((resp) => { + expect(resp.body.data.value).toBe(3); + }); + + await makeTestClient('/m/count', makeClientOptions, { where: { id: '1' } }) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data).toBe(1); + }); + + await makeTestClient('/m/count', makeClientOptions, {}) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data).toBe(2); + }); + + await makeTestClient('/m/aggregate', makeClientOptions, { _sum: { value: true } }) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data._sum.value).toBe(7); + }); + + await makeTestClient('/m/groupBy', makeClientOptions, { by: ['id'], _sum: { value: true } }) + .get('/') + .expect(200) + .expect((resp) => { + const data = resp.body.data; + expect(data).toHaveLength(2); + expect(data.find((item: any) => item.id === '1')._sum.value).toBe(4); + expect(data.find((item: any) => item.id === '2')._sum.value).toBe(3); + }); + + await makeTestClient('/m/delete', makeClientOptions, { where: { id: '1' } }) + .del('/') + .expect(200); + expect(await client.m.count()).toBe(1); + + await makeTestClient('/m/deleteMany', makeClientOptions, {}) + .del('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data.count).toBe(1); + }); + expect(await client.m.count()).toBe(0); + }); + + it('access policy crud', async () => { + const model = ` +model M { + id String @id @default(cuid()) + value Int + + @@allow('create,update', true) + @@allow('read', value > 0) + @@allow('post-update', value > 1) + @@allow('delete', value > 2) +} + `; + + const client = await createPolicyTestClient(model); + const makeClientOptions = { + getClient: () => client, + apiHandler: new RPCApiHandler({ + schema: client.$schema, + }), + }; + + await makeTestClient('/m/create', makeClientOptions) + .post('/') + .send({ data: { value: 0 } }) + .expect(403) + .expect((resp) => { + expect(resp.body.error.rejectReason).toBe('cannot-read-back'); + }); + + await makeTestClient('/m/create', makeClientOptions) + .post('/') + .send({ data: { id: '1', value: 1 } }) + .expect(201); + + await makeTestClient('/m/findMany', makeClientOptions) + .get('/') + .expect(200) + .expect((resp) => { + expect(resp.body.data).toHaveLength(1); + }); + + await makeTestClient('/m/update', makeClientOptions) + .put('/') + .send({ where: { id: '1' }, data: { value: 0 } }) + .expect(403); + + await makeTestClient('/m/update', makeClientOptions) + .put('/') + .send({ where: { id: '1' }, data: { value: 2 } }) + .expect(200); + + await makeTestClient('/m/delete', makeClientOptions, { where: { id: '1' } }) + .del('/') + .expect(404); + + await makeTestClient('/m/update', makeClientOptions) + .put('/') + .send({ where: { id: '1' }, data: { value: 3 } }) + .expect(200); + + await makeTestClient('/m/delete', makeClientOptions, { where: { id: '1' } }) + .del('/') + .expect(200); + }); +}); + +// describe('Next.js adapter tests - rest handler', () => { +// let origDir: string; + +// beforeEach(() => { +// origDir = process.cwd(); +// }); + +// afterEach(() => { +// process.chdir(origDir); +// }); + +// it('adapter test - rest', async () => { +// const model = ` +// model M { +// id String @id @default(cuid()) +// value Int +// } +// `; + +// const { prisma, modelMeta } = await loadSchema(model); + +// const options = { getPrisma: () => prisma, handler: Rest({ endpoint: 'http://localhost/api' }), modelMeta }; + +// await makeTestClient('/m', options) +// .post('/') +// .send({ data: { type: 'm', attributes: { id: '1', value: 1 } } }) +// .expect(201) +// .expect((resp) => { +// expect(resp.body.data.attributes.value).toBe(1); +// }); + +// await makeTestClient('/m/1', options) +// .get('/') +// .expect(200) +// .expect((resp) => { +// expect(resp.body.data.id).toBe('1'); +// }); + +// await makeTestClient('/m', options, undefined, { 'filter[value]': '1' }) +// .get('/') +// .expect(200) +// .expect((resp) => { +// expect(resp.body.data).toHaveLength(1); +// }); + +// await makeTestClient('/m', options, undefined, { 'filter[value]': '2' }) +// .get('/') +// .expect(200) +// .expect((resp) => { +// expect(resp.body.data).toHaveLength(0); +// }); + +// await makeTestClient('/m/1', options) +// .put('/') +// .send({ data: { type: 'm', attributes: { value: 2 } } }) +// .expect(200) +// .expect((resp) => { +// expect(resp.body.data.attributes.value).toBe(2); +// }); + +// await makeTestClient('/m/1', options).del('/').expect(200); +// expect(await prisma.m.count()).toBe(0); +// }); +// }); diff --git a/packages/server/tsup.config.ts b/packages/server/tsup.config.ts index 231bf08c8..d5d6d47c8 100644 --- a/packages/server/tsup.config.ts +++ b/packages/server/tsup.config.ts @@ -3,7 +3,8 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: { api: 'src/api/index.ts', - express: 'src/express/index.ts', + express: 'src/adapter/express/index.ts', + next: 'src/adapter/next/index.ts', }, outDir: 'dist', splitting: false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7682fa7b0..dfb4ed92d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -423,9 +423,6 @@ importers: decimal.js: specifier: 'catalog:' version: 10.4.3 - express: - specifier: ^5.0.0 - version: 5.1.0 superjson: specifier: ^2.2.3 version: 2.2.3 @@ -457,6 +454,12 @@ importers: body-parser: specifier: ^2.2.0 version: 2.2.0 + express: + specifier: ^5.0.0 + version: 5.1.0 + next: + specifier: ^15.0.0 + version: 15.5.6(react-dom@19.2.0(react@19.1.0))(react@19.1.0) supertest: specifier: ^7.1.4 version: 7.1.4 @@ -679,6 +682,9 @@ packages: '@chevrotain/utils@11.0.3': resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + '@emnapi/runtime@1.6.0': + resolution: {integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==} + '@esbuild/aix-ppc64@0.25.5': resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} @@ -891,6 +897,132 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -913,6 +1045,57 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@next/env@15.5.6': + resolution: {integrity: sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q==} + + '@next/swc-darwin-arm64@15.5.6': + resolution: {integrity: sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.6': + resolution: {integrity: sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.6': + resolution: {integrity: sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.6': + resolution: {integrity: sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.6': + resolution: {integrity: sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.6': + resolution: {integrity: sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.6': + resolution: {integrity: sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.6': + resolution: {integrity: sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@noble/hashes@1.7.1': resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} engines: {node: ^14.21.3 || >=16} @@ -1129,6 +1312,9 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/types@0.1.23': resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} @@ -1446,6 +1632,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + caniuse-lite@1.0.30001751: + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -1488,6 +1677,9 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -1618,6 +1810,10 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} @@ -2217,6 +2413,27 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + next@15.5.6: + resolution: {integrity: sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + 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 + node-abi@3.73.0: resolution: {integrity: sha512-z8iYzQGBu35ZkTQ9mtR8RqugJZ9RCLn8fv3d7LsgDBzOijGQP3RdKTX4LA7LXw03ZhU5z0l4xfhIMgSES31+cg==} engines: {node: '>=10'} @@ -2402,6 +2619,10 @@ packages: yaml: optional: true + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -2508,6 +2729,11 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + react@19.1.0: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} @@ -2557,6 +2783,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -2577,6 +2806,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2673,6 +2906,19 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -2767,6 +3013,9 @@ packages: ts-pattern@5.7.1: resolution: {integrity: sha512-EGs8PguQqAAUIcQfK4E9xdXxB6s2GK4sJfT/vcc9V1ELIvC4LH/zXu2t/5fajtv6oiRCxdv7BgtVK3vWgROxag==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.0: resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} engines: {node: '>=18'} @@ -3052,6 +3301,11 @@ snapshots: '@chevrotain/utils@11.0.3': {} + '@emnapi/runtime@1.6.0': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.25.5': optional: true @@ -3188,6 +3442,95 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.6.0 + optional: true + + '@img/sharp-win32-arm64@0.34.4': + optional: true + + '@img/sharp-win32-ia32@0.34.4': + optional: true + + '@img/sharp-win32-x64@0.34.4': + optional: true + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -3214,6 +3557,32 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@next/env@15.5.6': {} + + '@next/swc-darwin-arm64@15.5.6': + optional: true + + '@next/swc-darwin-x64@15.5.6': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.6': + optional: true + + '@next/swc-linux-arm64-musl@15.5.6': + optional: true + + '@next/swc-linux-x64-gnu@15.5.6': + optional: true + + '@next/swc-linux-x64-musl@15.5.6': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.6': + optional: true + + '@next/swc-win32-x64-msvc@15.5.6': + optional: true + '@noble/hashes@1.7.1': {} '@nodelib/fs.scandir@2.1.5': @@ -3377,6 +3746,10 @@ snapshots: '@swc/counter@0.1.3': optional: true + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + '@swc/types@0.1.23': dependencies: '@swc/counter': 0.1.3 @@ -3771,6 +4144,8 @@ snapshots: callsites@3.1.0: {} + caniuse-lite@1.0.30001751: {} + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -3818,6 +4193,8 @@ snapshots: cli-spinners@2.9.2: {} + client-only@0.0.1: {} + clone@1.0.4: {} color-convert@2.0.1: @@ -3908,6 +4285,9 @@ snapshots: detect-libc@2.0.3: {} + detect-libc@2.1.2: + optional: true + dezalgo@1.0.4: dependencies: asap: 2.0.6 @@ -4551,6 +4931,29 @@ snapshots: negotiator@1.0.0: {} + next@15.5.6(react-dom@19.2.0(react@19.1.0))(react@19.1.0): + dependencies: + '@next/env': 15.5.6 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001751 + postcss: 8.4.31 + react: 19.1.0 + react-dom: 19.2.0(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.6 + '@next/swc-darwin-x64': 15.5.6 + '@next/swc-linux-arm64-gnu': 15.5.6 + '@next/swc-linux-arm64-musl': 15.5.6 + '@next/swc-linux-x64-gnu': 15.5.6 + '@next/swc-linux-x64-musl': 15.5.6 + '@next/swc-win32-arm64-msvc': 15.5.6 + '@next/swc-win32-x64-msvc': 15.5.6 + sharp: 0.34.4 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + node-abi@3.73.0: dependencies: semver: 7.7.2 @@ -4726,6 +5129,12 @@ snapshots: tsx: 4.20.3 yaml: 2.8.0 + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -4829,6 +5238,11 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-dom@19.2.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.27.0 + react@19.1.0: {} readable-stream@3.6.2: @@ -4896,6 +5310,8 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.27.0: {} + semver@7.7.2: {} send@1.2.0: @@ -4934,6 +5350,36 @@ snapshots: setprototypeof@1.2.0: {} + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5030,6 +5476,11 @@ snapshots: dependencies: js-tokens: 9.0.1 + styled-jsx@5.1.6(react@19.1.0): + dependencies: + client-only: 0.0.1 + react: 19.1.0 + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -5133,6 +5584,8 @@ snapshots: ts-pattern@5.7.1: {} + tslib@2.8.1: {} + tsup@8.5.0(@swc/core@1.12.5)(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0): dependencies: bundle-require: 5.1.0(esbuild@0.25.5)