diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 77be062a3163..0e101ba1e744 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -19,6 +19,8 @@ import { } from '../types/private.js'; import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types'; import type { PluginOptions } from '@sveltejs/vite-plugin-svelte'; +import type { IncomingMessage } from 'node:http'; +import type { Duplex } from 'node:stream'; export { PrerenderOption } from '../types/private.js'; @@ -685,8 +687,8 @@ export interface KitConfig { */ export type Handle = (input: { event: RequestEvent; - resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise; -}) => MaybePromise; + resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise; +}) => MaybePromise; /** * The server-side [`handleError`](https://svelte.dev/docs/kit/hooks#shared-hooks-handleError) hook runs when an unexpected error is thrown while responding to a request. @@ -1076,6 +1078,10 @@ export interface RequestEvent< * The original request object */ request: Request; + /** + * The upgrade request object + */ + upgrade: { request: IncomingMessage; socket: Duplex; head: Buffer } | null; /** * Info about the current route */ @@ -1133,6 +1139,16 @@ export type RequestHandler< RouteId extends string | null = string | null > = (event: RequestEvent) => MaybePromise; +/** + * A `(event: UpgradeEvent) => void` function exported from a `+server.js` file with the name UPGRADE and handles server upgrade requests. + * + * It receives `Params` as the first generic argument, which you can skip by using [generated types](https://svelte.dev/docs/kit/types#Generated-types) instead. + */ +export type UpgradeHandler< + Params extends Partial> = Partial>, + RouteId extends string | null = string | null +> = (event: RequestEvent) => MaybePromise; + export interface ResolveOptions { /** * Applies custom transforms to HTML. If `done` is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 0dbc912940a2..eda1f1298f9a 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -431,6 +431,52 @@ export async function dev(vite, vite_config, svelte_config) { // serving routes with those names. See https://github.com/vitejs/vite/issues/7363 remove_static_middlewares(vite.middlewares); + vite.httpServer?.on('upgrade', async (req, socket, head) => { + const base = `${vite.config.server.https ? 'wss' : 'ws'}://${ + req.headers[':authority'] || req.headers.host + }`; + + // we have to import `Server` before calling `set_assets` + const { Server } = /** @type {import('types').ServerModule} */ ( + await vite.ssrLoadModule(`${runtime_base}/server/index.js`, { fixStacktrace: true }) + ); + + const server = new Server(manifest); + + await server.init({ + env, + read: (file) => createReadableStream(from_fs(file)) + }); + + const request = await getRequest({ + base, + request: req + }); + + await server.respond( + request, + { + getClientAddress: () => { + const { remoteAddress } = req.socket; + if (remoteAddress) return remoteAddress; + throw new Error('Could not determine clientAddress'); + }, + read: (file) => { + if (file in manifest._.server_assets) { + return fs.readFileSync(from_fs(file)); + } + + return fs.readFileSync(path.join(svelte_config.kit.files.assets, file)); + }, + before_handle: (event, config, prerender) => { + async_local_storage.enterWith({ event, config, prerender }); + }, + emulator + }, + { request: req, socket, head } + ); + }); + vite.middlewares.use(async (req, res) => { // Vite's base middleware strips out the base path. Restore it const original_url = req.url; diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 55bcd87807b9..c3614e6da46d 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -7,17 +7,25 @@ import { method_not_allowed } from './utils.js'; * @param {import('@sveltejs/kit').RequestEvent} event * @param {import('types').SSREndpoint} mod * @param {import('types').SSRState} state - * @returns {Promise} + * @returns {Promise} */ export async function render_endpoint(event, mod, state) { const method = /** @type {import('types').HttpMethod} */ (event.request.method); + /** + * The handler function to use for the request + * @type {import('@sveltejs/kit').RequestHandler | import('@sveltejs/kit').UpgradeHandler | undefined} + */ let handler = mod[method] || mod.fallback; if (method === 'HEAD' && mod.GET && !mod.HEAD) { handler = mod.GET; } + if (method === 'GET' && !mod.GET && mod.UPGRADE) { + handler = mod.UPGRADE; + } + if (!handler) { return method_not_allowed(mod, method); } @@ -40,6 +48,13 @@ export async function render_endpoint(event, mod, state) { } try { + if (method === 'GET' && event.request.headers.has('upgrade') && event.upgrade && mod.UPGRADE) { + await handler( + /** @type {import('@sveltejs/kit').RequestEvent>} */ (event) + ); + return; + } + let response = await handler( /** @type {import('@sveltejs/kit').RequestEvent>} */ (event) ); diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 36cbd04be16f..c95d68370cf7 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -93,12 +93,19 @@ export class Server { /** * @param {Request} request * @param {import('types').RequestOptions} options + * @param {{request: import('http').IncomingMessage, socket: import('stream').Duplex , head: Buffer}?} webhookRequest */ - async respond(request, options) { - return respond(request, this.#options, this.#manifest, { - ...options, - error: false, - depth: 0 - }); + async respond(request, options, webhookRequest) { + return respond( + request, + this.#options, + this.#manifest, + { + ...options, + error: false, + depth: 0 + }, + webhookRequest + ); } } diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index f81f52ef2557..852aee368d1d 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -54,9 +54,10 @@ const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']); * @param {import('types').SSROptions} options * @param {import('@sveltejs/kit').SSRManifest} manifest * @param {import('types').SSRState} state - * @returns {Promise} + * @param {{request: import('http').IncomingMessage, socket: import('stream').Duplex , head: Buffer}?} upgradeRequest + * @returns {Promise} */ -export async function respond(request, options, manifest, state) { +export async function respond(request, options, manifest, state, upgradeRequest) { /** URL but stripped from the potential `/__data.json` suffix and its search param */ const url = new URL(request.url); @@ -139,7 +140,7 @@ export async function respond(request, options, manifest, state) { } if (!state.prerendering?.fallback) { - // TODO this could theoretically break — should probably be inside a try-catch + // TODO this could theoretically break - should probably be inside a try-catch const matchers = await manifest._.matchers(); for (const candidate of manifest._.routes) { @@ -181,6 +182,7 @@ export async function respond(request, options, manifest, state) { params, platform: state.platform, request, + upgrade: upgradeRequest || null, route: { id: route?.id ?? null }, setHeaders: (new_headers) => { for (const key in new_headers) { @@ -328,6 +330,7 @@ export async function respond(request, options, manifest, state) { event, resolve: (event, opts) => resolve(event, opts).then((response) => { + if (!response) return; // add headers/cookies here, rather than inside `resolve`, so that we // can do it once for all responses instead of once per `return` for (const key in headers) { @@ -346,7 +349,7 @@ export async function respond(request, options, manifest, state) { }); // respond with 304 if etag matches - if (response.status === 200 && response.headers.has('etag')) { + if (response?.status === 200 && response?.headers.has('etag')) { let if_none_match_value = request.headers.get('if-none-match'); // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives @@ -381,7 +384,7 @@ export async function respond(request, options, manifest, state) { // Edge case: If user does `return Response(30x)` in handle hook while processing a data request, // we need to transform the redirect response to a corresponding JSON response. - if (is_data_request && response.status >= 300 && response.status <= 308) { + if (is_data_request && response && response.status >= 300 && response.status <= 308) { const location = response.headers.get('location'); if (location) { return redirect_json_response(new Redirect(/** @type {any} */ (response.status), location)); @@ -434,7 +437,7 @@ export async function respond(request, options, manifest, state) { if (route) { const method = /** @type {import('types').HttpMethod} */ (event.request.method); - /** @type {Response} */ + /** @type {void | Response} */ let response; if (is_data_request) { @@ -484,7 +487,7 @@ export async function respond(request, options, manifest, state) { // If the route contains a page and an endpoint, we need to add a // `Vary: Accept` header to the response because of browser caching - if (request.method === 'GET' && route.page && route.endpoint) { + if (request.method === 'GET' && route.page && route.endpoint && response) { const vary = response.headers .get('vary') ?.split(',') diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index c9dbb51ce007..7b0ef17cec3e 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -17,7 +17,8 @@ import { RequestEvent, SSRManifest, Emulator, - Adapter + Adapter, + UpgradeHandler } from '@sveltejs/kit'; import { HttpMethod, @@ -26,6 +27,8 @@ import { RequestOptions, TrailingSlash } from './private.js'; +import type { IncomingMessage } from 'node:http'; +import type { Duplex } from 'node:stream'; export interface ServerModule { Server: typeof InternalServer; @@ -131,7 +134,8 @@ export class InternalServer extends Server { /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated */ before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void; emulator?: Emulator; - } + }, + webhookRequest?: { request: IncomingMessage; socket: Duplex; head: Buffer } ): Promise; } @@ -386,6 +390,7 @@ export interface PageNodeIndexes { export type PrerenderEntryGenerator = () => MaybePromise>>; export type SSREndpoint = Partial> & { + UPGRADE?: UpgradeHandler; prerender?: PrerenderOption; trailingSlash?: TrailingSlash; config?: any; diff --git a/packages/kit/src/utils/exports.js b/packages/kit/src/utils/exports.js index ed685edb7ded..93860c5b5377 100644 --- a/packages/kit/src/utils/exports.js +++ b/packages/kit/src/utils/exports.js @@ -72,6 +72,7 @@ const valid_page_exports = new Set([...valid_layout_exports, 'entries']); const valid_layout_server_exports = new Set([...valid_layout_exports]); const valid_page_server_exports = new Set([...valid_layout_server_exports, 'actions', 'entries']); const valid_server_exports = new Set([ + 'UPGRADE', 'GET', 'POST', 'PATCH', diff --git a/packages/kit/src/utils/exports.spec.js b/packages/kit/src/utils/exports.spec.js index e27817c17b5c..4f0e52781d40 100644 --- a/packages/kit/src/utils/exports.spec.js +++ b/packages/kit/src/utils/exports.spec.js @@ -174,7 +174,7 @@ test('validates +server.js', () => { validate_server_exports({ answer: 42 }); - }, "Invalid export 'answer' (valid exports are GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD, fallback, prerender, trailingSlash, config, entries, or anything with a '_' prefix)"); + }, "Invalid export 'answer' (valid exports are UPGRADE, GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD, fallback, prerender, trailingSlash, config, entries, or anything with a '_' prefix)"); check_error(() => { validate_server_exports({ diff --git a/packages/kit/test/apps/options-2/package.json b/packages/kit/test/apps/options-2/package.json index 963d87a3c03e..79d850f540da 100644 --- a/packages/kit/test/apps/options-2/package.json +++ b/packages/kit/test/apps/options-2/package.json @@ -16,10 +16,13 @@ "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "^3.0.1", "cross-env": "^7.0.3", + "socket.io": "^4.8.1", + "socket.io-client": "^4.8.1", "svelte": "^4.2.10", "svelte-check": "^4.0.1", "typescript": "^5.3.3", - "vite": "^5.3.2" + "vite": "^5.3.2", + "ws": "^8.18.0" }, "type": "module" } diff --git a/packages/kit/test/apps/options-2/src/routes/+page.svelte b/packages/kit/test/apps/options-2/src/routes/+page.svelte index badacc8173d8..a8faccaba6e3 100644 --- a/packages/kit/test/apps/options-2/src/routes/+page.svelte +++ b/packages/kit/test/apps/options-2/src/routes/+page.svelte @@ -8,3 +8,7 @@

assets: {assets}

Go to /hello +
+Go to /ws +
+Go to /socket.io diff --git a/packages/kit/test/apps/options-2/src/routes/ws/+page.svelte b/packages/kit/test/apps/options-2/src/routes/ws/+page.svelte new file mode 100644 index 000000000000..d68ebdcc35c3 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/ws/+page.svelte @@ -0,0 +1,35 @@ + + +

Messages:

+ +
+ {#each messages as message} +

{message}

+ {/each} +
diff --git a/packages/kit/test/apps/options-2/src/routes/ws/+server.js b/packages/kit/test/apps/options-2/src/routes/ws/+server.js new file mode 100644 index 000000000000..c839e67c0fb1 --- /dev/null +++ b/packages/kit/test/apps/options-2/src/routes/ws/+server.js @@ -0,0 +1,19 @@ +import { WebSocketServer } from 'ws'; + +const wss = new WebSocketServer({ noServer: true }); + +wss.on('connection', (ws) => { + ws.on('error', console.error); + + ws.on('message', (message) => { + console.log('received: %s', message); + ws.send(String(message)); + }); +}); + +export function UPGRADE({ upgrade }) { + wss.handleUpgrade(upgrade.request, upgrade.socket, upgrade.head, (ws) => { + console.log('UPGRADED'); + wss.emit('connection', ws, upgrade.request); + }); +} diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 491de54d9698..d352912d5d9c 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -4,6 +4,8 @@ declare module '@sveltejs/kit' { import type { CompileOptions } from 'svelte/compiler'; import type { PluginOptions } from '@sveltejs/vite-plugin-svelte'; + import type { IncomingMessage } from 'node:http'; + import type { Duplex } from 'node:stream'; /** * [Adapters](https://svelte.dev/docs/kit/adapters) are responsible for taking the production build and turning it into something that can be deployed to a platform of your choosing. */ @@ -667,8 +669,8 @@ declare module '@sveltejs/kit' { */ export type Handle = (input: { event: RequestEvent; - resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise; - }) => MaybePromise; + resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise; + }) => MaybePromise; /** * The server-side [`handleError`](https://svelte.dev/docs/kit/hooks#shared-hooks-handleError) hook runs when an unexpected error is thrown while responding to a request. @@ -1058,6 +1060,10 @@ declare module '@sveltejs/kit' { * The original request object */ request: Request; + /** + * The upgrade request object + */ + upgrade: { request: IncomingMessage; socket: Duplex; head: Buffer } | null; /** * Info about the current route */ @@ -1115,6 +1121,16 @@ declare module '@sveltejs/kit' { RouteId extends string | null = string | null > = (event: RequestEvent) => MaybePromise; + /** + * A `(event: UpgradeEvent) => void` function exported from a `+server.js` file with the name UPGRADE and handles server upgrade requests. + * + * It receives `Params` as the first generic argument, which you can skip by using [generated types](https://svelte.dev/docs/kit/types#Generated-types) instead. + */ + export type UpgradeHandler< + Params extends Partial> = Partial>, + RouteId extends string | null = string | null + > = (event: RequestEvent) => MaybePromise; + export interface ResolveOptions { /** * Applies custom transforms to HTML. If `done` is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML @@ -1734,6 +1750,7 @@ declare module '@sveltejs/kit' { type PrerenderEntryGenerator = () => MaybePromise>>; type SSREndpoint = Partial> & { + UPGRADE?: UpgradeHandler; prerender?: PrerenderOption; trailingSlash?: TrailingSlash; config?: any; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a360411ab50..d0b1290e9165 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -607,6 +607,12 @@ importers: cross-env: specifier: ^7.0.3 version: 7.0.3 + socket.io: + specifier: ^4.8.1 + version: 4.8.1 + socket.io-client: + specifier: ^4.8.1 + version: 4.8.1 svelte: specifier: ^4.2.10 version: 4.2.19 @@ -619,6 +625,9 @@ importers: vite: specifier: ^5.3.2 version: 5.3.6(@types/node@18.19.50)(lightningcss@1.24.1) + ws: + specifier: ^8.18.0 + version: 8.18.0 packages/kit/test/apps/writes: devDependencies: @@ -1911,6 +1920,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@stylistic/eslint-plugin-js@2.1.0': resolution: {integrity: sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1962,9 +1974,15 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.4.1': + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cors@2.8.17': + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} @@ -2078,6 +2096,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2168,6 +2190,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -2283,6 +2309,14 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -2393,6 +2427,17 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + engine.io-client@6.6.2: + resolution: {integrity: sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.2: + resolution: {integrity: sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==} + engines: {node: '>=10.2.0'} + enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -2954,6 +2999,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -3023,6 +3076,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -3436,6 +3493,21 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3703,6 +3775,10 @@ packages: engines: {node: '>=8'} hasBin: true + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vite-imagetools@7.0.1: resolution: {integrity: sha512-23jnLhkTH0HR9Vd9LxMYnajOLeo0RJNEAHhtlsQP6kfPuOBoTzt54rWbEWB9jmhEXAOflLQpM+FrmilVPAoyGA==} engines: {node: '>=18.0.0'} @@ -3818,6 +3894,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -3830,6 +3918,10 @@ packages: utf-8-validate: optional: true + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + xxhash-wasm@1.0.2: resolution: {integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==} @@ -4493,6 +4585,8 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@socket.io/component-emitter@3.1.2': {} + '@stylistic/eslint-plugin-js@2.1.0(eslint@9.6.0)': dependencies: '@types/eslint': 8.56.12 @@ -4593,8 +4687,14 @@ snapshots: dependencies: '@types/node': 18.19.50 + '@types/cookie@0.4.1': {} + '@types/cookie@0.6.0': {} + '@types/cors@2.8.17': + dependencies: + '@types/node': 18.19.50 + '@types/eslint@8.56.12': dependencies: '@types/estree': 1.0.6 @@ -4760,6 +4860,11 @@ snapshots: abbrev@1.1.1: {} + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: acorn: 8.12.1 @@ -4833,6 +4938,8 @@ snapshots: balanced-match@1.0.2: {} + base64id@2.0.0: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -4948,6 +5055,13 @@ snapshots: cookie@0.6.0: {} + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cross-env@7.0.3: dependencies: cross-spawn: 7.0.3 @@ -5033,6 +5147,37 @@ snapshots: emoji-regex@8.0.0: {} + engine.io-client@6.6.2: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + engine.io@6.6.2: + dependencies: + '@types/cookie': 0.4.1 + '@types/cors': 2.8.17 + '@types/node': 18.19.50 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.5 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 @@ -5638,6 +5783,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + mime@3.0.0: {} mimic-fn@4.0.0: {} @@ -5700,6 +5851,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -6089,6 +6242,47 @@ snapshots: slash@3.0.0: {} + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.5 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-client@4.8.1: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + engine.io-client: 6.6.2 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.5 + engine.io: 6.6.2 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + source-map-js@1.2.1: {} source-map@0.6.1: {} @@ -6344,6 +6538,8 @@ snapshots: kleur: 4.1.5 sade: 1.8.1 + vary@1.1.2: {} + vite-imagetools@7.0.1(rollup@4.24.0): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.24.0) @@ -6478,8 +6674,12 @@ snapshots: wrappy@1.0.2: {} + ws@8.17.1: {} + ws@8.18.0: {} + xmlhttprequest-ssl@2.1.2: {} + xxhash-wasm@1.0.2: {} yallist@2.1.2: {}