diff --git a/packages/server/package.json b/packages/server/package.json index dde13ffb7..cc466e812 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -21,7 +21,8 @@ "express", "nextjs", "sveltekit", - "nuxtjs" + "nuxtjs", + "elysia" ], "author": "ZenStack Team", "license": "MIT", @@ -48,6 +49,7 @@ "@types/supertest": "^2.0.12", "@zenstackhq/testtools": "workspace:*", "body-parser": "^1.20.2", + "elysia": "^1.3.1", "express": "^4.19.2", "fastify": "^4.14.1", "fastify-plugin": "^4.5.0", diff --git a/packages/server/src/elysia/handler.ts b/packages/server/src/elysia/handler.ts new file mode 100644 index 000000000..f3bb72b1d --- /dev/null +++ b/packages/server/src/elysia/handler.ts @@ -0,0 +1,82 @@ +import { DbClientContract } from '@zenstackhq/runtime'; +import { Elysia, Context as ElysiaContext } from 'elysia'; +import { RPCApiHandler } from '../api'; +import { loadAssets } from '../shared'; +import { AdapterBaseOptions } from '../types'; + +/** + * Options for initializing an Elysia middleware. + */ +export interface ElysiaOptions extends AdapterBaseOptions { + /** + * Callback method for getting a Prisma instance for the given request context. + */ + getPrisma: (context: ElysiaContext) => Promise | unknown; + /** + * Optional base path to strip from the request path before passing to the API handler. + */ + basePath?: string; +} + +/** + * Creates an Elysia middleware handler for ZenStack. + * This handler provides automatic CRUD APIs through Elysia's routing system. + */ +export function createElysiaHandler(options: ElysiaOptions) { + const { modelMeta, zodSchemas } = loadAssets(options); + const requestHandler = options.handler ?? RPCApiHandler(); + + return async (app: Elysia) => { + app.all('/*', async (ctx: ElysiaContext) => { + const { request, body, set } = ctx; + const prisma = (await options.getPrisma(ctx)) as DbClientContract; + if (!prisma) { + set.status = 500; + return { + message: 'unable to get prisma from request context', + }; + } + + const url = new URL(request.url); + const query = Object.fromEntries(url.searchParams); + let path = url.pathname; + + if (options.basePath && path.startsWith(options.basePath)) { + path = path.slice(options.basePath.length); + if (!path.startsWith('/')) { + path = '/' + path; + } + } + + if (!path || path === '/') { + set.status = 400; + return { + message: 'missing path parameter', + }; + } + + try { + const r = await requestHandler({ + method: request.method, + path, + query, + requestBody: body, + prisma, + modelMeta, + zodSchemas, + logger: options.logger, + }); + + set.status = r.status; + return r.body; + } catch (err) { + set.status = 500; + return { + message: 'An internal server error occurred', + }; + } + }); + + return app; + }; +} diff --git a/packages/server/src/elysia/index.ts b/packages/server/src/elysia/index.ts new file mode 100644 index 000000000..bc0d7e165 --- /dev/null +++ b/packages/server/src/elysia/index.ts @@ -0,0 +1 @@ +export * from './handler'; \ No newline at end of file diff --git a/packages/server/tests/adapter/elysia.test.ts b/packages/server/tests/adapter/elysia.test.ts new file mode 100644 index 000000000..f45b7c039 --- /dev/null +++ b/packages/server/tests/adapter/elysia.test.ts @@ -0,0 +1,200 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/// +import { loadSchema } from '@zenstackhq/testtools'; +import 'isomorphic-fetch'; +import path from 'path'; +import superjson from 'superjson'; +import Rest from '../../src/api/rest'; +import { createElysiaHandler } from '../../src/elysia'; +import { makeUrl, schema } from '../utils'; +import { Elysia } from 'elysia'; + +describe('Elysia adapter tests - rpc handler', () => { + it('run hooks regular json', async () => { + const { prisma, zodSchemas } = await loadSchema(schema); + + const handler = await createElysiaApp( + createElysiaHandler({ getPrisma: () => prisma, zodSchemas, basePath: '/api' }) + ); + + let r = await handler(makeRequest('GET', makeUrl('/api/post/findMany', { where: { id: { equals: '1' } } }))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(0); + + r = await handler( + makeRequest('POST', '/api/user/create', { + include: { posts: true }, + data: { + id: 'user1', + email: 'user1@abc.com', + posts: { + create: [ + { title: 'post1', published: true, viewCount: 1 }, + { title: 'post2', published: false, viewCount: 2 }, + ], + }, + }, + }) + ); + expect(r.status).toBe(201); + expect((await unmarshal(r)).data).toMatchObject({ + email: 'user1@abc.com', + posts: expect.arrayContaining([ + expect.objectContaining({ title: 'post1' }), + expect.objectContaining({ title: 'post2' }), + ]), + }); + + r = await handler(makeRequest('GET', makeUrl('/api/post/findMany'))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(2); + + r = await handler(makeRequest('GET', makeUrl('/api/post/findMany', { where: { viewCount: { gt: 1 } } }))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(1); + + r = await handler( + makeRequest('PUT', '/api/user/update', { where: { id: 'user1' }, data: { email: 'user1@def.com' } }) + ); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data.email).toBe('user1@def.com'); + + r = await handler(makeRequest('GET', makeUrl('/api/post/count', { where: { viewCount: { gt: 1 } } }))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toBe(1); + + r = await handler(makeRequest('GET', makeUrl('/api/post/aggregate', { _sum: { viewCount: true } }))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data._sum.viewCount).toBe(3); + + r = await handler( + makeRequest('GET', makeUrl('/api/post/groupBy', { by: ['published'], _sum: { viewCount: true } })) + ); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ published: true, _sum: { viewCount: 1 } }), + expect.objectContaining({ published: false, _sum: { viewCount: 2 } }), + ]) + ); + + r = await handler(makeRequest('DELETE', makeUrl('/api/user/deleteMany', { where: { id: 'user1' } }))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data.count).toBe(1); + }); + + it('custom load path', async () => { + const { prisma, projectDir } = await loadSchema(schema, { output: './zen' }); + + const handler = await createElysiaApp( + createElysiaHandler({ + getPrisma: () => prisma, + basePath: '/api', + modelMeta: require(path.join(projectDir, './zen/model-meta')).default, + zodSchemas: require(path.join(projectDir, './zen/zod')), + }) + ); + + const r = await handler( + makeRequest('POST', '/api/user/create', { + include: { posts: true }, + data: { + id: 'user1', + email: 'user1@abc.com', + posts: { + create: [ + { title: 'post1', published: true, viewCount: 1 }, + { title: 'post2', published: false, viewCount: 2 }, + ], + }, + }, + }) + ); + expect(r.status).toBe(201); + }); +}); + +describe('Elysia adapter tests - rest handler', () => { + it('run hooks', async () => { + const { prisma, modelMeta, zodSchemas } = await loadSchema(schema); + + const handler = await createElysiaApp( + createElysiaHandler({ + getPrisma: () => prisma, + basePath: '/api', + handler: Rest({ endpoint: 'http://localhost/api' }), + modelMeta, + zodSchemas, + }) + ); + + let r = await handler(makeRequest('GET', makeUrl('/api/post/1'))); + expect(r.status).toBe(404); + + r = await handler( + makeRequest('POST', '/api/user', { + data: { + type: 'user', + attributes: { id: 'user1', email: 'user1@abc.com' }, + }, + }) + ); + expect(r.status).toBe(201); + expect(await unmarshal(r)).toMatchObject({ + data: { + id: 'user1', + attributes: { + email: 'user1@abc.com', + }, + }, + }); + + r = await handler(makeRequest('GET', makeUrl('/api/user?filter[id]=user1'))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(1); + + r = await handler(makeRequest('GET', makeUrl('/api/user?filter[id]=user2'))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(0); + + r = await handler(makeRequest('GET', makeUrl('/api/user?filter[id]=user1&filter[email]=xyz'))); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data).toHaveLength(0); + + r = await handler( + makeRequest('PUT', makeUrl('/api/user/user1'), { + data: { type: 'user', attributes: { email: 'user1@def.com' } }, + }) + ); + expect(r.status).toBe(200); + expect((await unmarshal(r)).data.attributes.email).toBe('user1@def.com'); + + r = await handler(makeRequest('DELETE', makeUrl(makeUrl('/api/user/user1')))); + expect(r.status).toBe(200); + expect(await prisma.user.findMany()).toHaveLength(0); + }); +}); + +function makeRequest(method: string, path: string, body?: any) { + if (body) { + return new Request(`http://localhost${path}`, { + method, + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' }, + }); + } else { + return new Request(`http://localhost${path}`, { method }); + } +} + +async function unmarshal(r: Response, useSuperJson = false) { + const text = await r.text(); + return (useSuperJson ? superjson.parse(text) : JSON.parse(text)) as any; +} + +async function createElysiaApp(middleware: (app: Elysia) => Promise) { + const app = new Elysia(); + await middleware(app); + return app.handle; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e98dbcd2..11951dc6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -725,6 +725,9 @@ importers: body-parser: specifier: ^1.20.2 version: 1.20.2 + elysia: + specifier: ^1.3.1 + version: 1.3.1(exact-mirror@0.1.2(@sinclair/typebox@0.34.33))(file-type@20.5.0)(typescript@5.5.2) express: specifier: ^4.19.2 version: 4.19.2 @@ -2716,6 +2719,7 @@ packages: '@readme/json-schema-ref-parser@1.2.0': resolution: {integrity: sha512-Bt3QVovFSua4QmHa65EHUmh2xS0XJ3rgTEUPH998f4OW4VVJke3BuS16f+kM0ZLOGdvIrzrPRqwihuv5BAjtrA==} + deprecated: This package is no longer maintained. Please use `@apidevtools/json-schema-ref-parser` instead. '@readme/openapi-parser@2.6.0': resolution: {integrity: sha512-pyFJXezWj9WI1O+gdp95CoxfY+i+Uq3kKk4zXIFuRAZi9YnHpHOpjumWWr67wkmRTw19Hskh9spyY0Iyikf3fA==} @@ -2919,6 +2923,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sinclair/typebox@0.34.33': + resolution: {integrity: sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g==} + '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} @@ -3025,6 +3032,13 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -4141,7 +4155,7 @@ packages: engines: {node: '>= 14'} concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -4193,6 +4207,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} @@ -4383,6 +4401,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize@2.0.0: resolution: {integrity: sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==} engines: {node: '>=4'} @@ -4579,11 +4606,18 @@ packages: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} ee-first@1.1.1: - resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} electron-to-chromium@1.4.814: resolution: {integrity: sha512-GVulpHjFu1Y9ZvikvbArHmAhZXtm3wHlpjTMcXNGKl4IQ4jMQjlnz8yMQYYqdLHKi/jEL2+CBC2akWVCoIGUdw==} + elysia@1.3.1: + resolution: {integrity: sha512-En41P6cDHcHtQ0nvfsn9ayB+8ahQJqG1nzvPX8FVZjOriFK/RtZPQBtXMfZDq/AsVIk7JFZGFEtAVEmztNJVhQ==} + peerDependencies: + exact-mirror: '>= 0.0.9' + file-type: '>= 20.0.0' + typescript: '>= 5.0.0' + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -4792,6 +4826,14 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + exact-mirror@0.1.2: + resolution: {integrity: sha512-wFCPCDLmHbKGUb8TOi/IS7jLsgR8WVDGtDK3CzcB4Guf/weq7G+I+DkXiRSZfbemBFOxOINKpraM6ml78vo8Zw==} + peerDependencies: + '@sinclair/typebox': ^0.34.15 + peerDependenciesMeta: + '@sinclair/typebox': + optional: true + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -4889,6 +4931,9 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -4897,6 +4942,10 @@ packages: resolution: {integrity: sha512-mQ6dqz+z59on3B50IGF3ujNGbZmY1TAeLHpNfhLEeNM6Lky31w3RUlbCyqZWQs0DuZJQU4R2qDuVd9ojyzadcg==} engines: {node: '>=12.17'} + file-type@20.5.0: + resolution: {integrity: sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==} + engines: {node: '>=18'} + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -6145,7 +6194,7 @@ packages: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} media-typer@0.3.0: - resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} merge-descriptors@1.0.1: @@ -6682,6 +6731,10 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + peek-readable@7.0.0: + resolution: {integrity: sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==} + engines: {node: '>=18'} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -7711,6 +7764,10 @@ packages: strip-literal@2.1.0: resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strtok3@10.2.2: + resolution: {integrity: sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==} + engines: {node: '>=18'} + styled-jsx@5.1.1: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -7917,6 +7974,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@6.0.0: + resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} + engines: {node: '>=14.16'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -8128,6 +8189,10 @@ packages: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} + uint8array-extras@1.4.0: + resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==} + engines: {node: '>=18'} + ultrahtml@1.5.3: resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==} @@ -8305,7 +8370,7 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} utils-merge@1.0.1: - resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} uuid@10.0.0: @@ -11151,6 +11216,9 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@sinclair/typebox@0.34.33': + optional: true + '@sindresorhus/merge-streams@2.3.0': {} '@sinonjs/commons@3.0.1': @@ -11278,6 +11346,16 @@ snapshots: react: 18.2.0 react-dom: 18.3.1(react@18.2.0) + '@tokenizer/inflate@0.2.7': + dependencies: + debug: 4.4.1 + fflate: 0.8.2 + token-types: 6.0.0 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + '@tootallnate/once@2.0.0': {} '@trpc/client@10.45.2(@trpc/server@10.45.2)': @@ -12673,6 +12751,8 @@ snapshots: cookie@0.6.0: {} + cookie@1.0.2: {} + cookiejar@2.1.4: {} copy-anything@3.0.5: @@ -12882,6 +12962,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.4.1: + dependencies: + ms: 2.1.3 + decamelize@2.0.0: dependencies: xregexp: 4.0.0 @@ -13057,6 +13141,17 @@ snapshots: electron-to-chromium@1.4.814: {} + elysia@1.3.1(exact-mirror@0.1.2(@sinclair/typebox@0.34.33))(file-type@20.5.0)(typescript@5.5.2): + dependencies: + cookie: 1.0.2 + exact-mirror: 0.1.2(@sinclair/typebox@0.34.33) + fast-decode-uri-component: 1.0.1 + file-type: 20.5.0 + typescript: 5.5.2 + optionalDependencies: + '@sinclair/typebox': 0.34.33 + openapi-types: 12.1.3 + emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -13416,6 +13511,10 @@ snapshots: events@3.3.0: {} + exact-mirror@0.1.2(@sinclair/typebox@0.34.33): + optionalDependencies: + '@sinclair/typebox': 0.34.33 + execa@5.1.1: dependencies: cross-spawn: 7.0.3 @@ -13595,6 +13694,8 @@ snapshots: dependencies: pend: 1.2.0 + fflate@0.8.2: {} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -13604,6 +13705,15 @@ snapshots: array-back: 6.2.2 glob: 7.2.3 + file-type@20.5.0: + dependencies: + '@tokenizer/inflate': 0.2.7 + strtok3: 10.2.2 + token-types: 6.0.0 + uint8array-extras: 1.4.0 + transitivePeerDependencies: + - supports-color + file-uri-to-path@1.0.0: {} fill-keys@1.0.2: @@ -15856,6 +15966,8 @@ snapshots: pathval@1.1.1: {} + peek-readable@7.0.0: {} + pend@1.2.0: {} perfect-debounce@1.0.0: {} @@ -16932,6 +17044,11 @@ snapshots: dependencies: js-tokens: 9.0.0 + strtok3@10.2.2: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 7.0.0 + styled-jsx@5.1.1(@babel/core@7.24.7)(react@18.2.0): dependencies: client-only: 0.0.1 @@ -17169,6 +17286,11 @@ snapshots: toidentifier@1.0.1: {} + token-types@6.0.0: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + totalist@3.0.1: {} tough-cookie@4.1.4: @@ -17389,6 +17511,8 @@ snapshots: dependencies: '@lukeed/csprng': 1.1.0 + uint8array-extras@1.4.0: {} + ultrahtml@1.5.3: {} unbox-primitive@1.0.2: