Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 34 additions & 18 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -315,7 +317,7 @@ export interface KitConfig {
/**
* The directory where SvelteKit keeps its stuff, including static assets (such as JS and CSS) and internally-used routes.
*
* If `paths.assets` is specified, there will be two app directories `${paths.assets}/${appDir}` and `${paths.base}/${appDir}`.
* If `paths.assets` is specified, there will be two app directories - `${paths.assets}/${appDir}` and `${paths.base}/${appDir}`.
* @default "_app"
*/
appDir?: string;
Expand Down Expand Up @@ -548,10 +550,10 @@ export interface KitConfig {
/**
* How to respond to HTTP errors encountered while prerendering the app.
*
* - `'fail'` fail the build
* - `'fail'` - fail the build
* - `'ignore'` - silently ignore the failure and continue
* - `'warn'` continue, but print a warning
* - `(details) => void` a custom error handler that takes a `details` object with `status`, `path`, `referrer`, `referenceType` and `message` properties. If you `throw` from this function, the build will fail
* - `'warn'` - continue, but print a warning
* - `(details) => void` - a custom error handler that takes a `details` object with `status`, `path`, `referrer`, `referenceType` and `message` properties. If you `throw` from this function, the build will fail
*
* ```js
* /// file: svelte.config.js
Expand Down Expand Up @@ -580,10 +582,10 @@ export interface KitConfig {
/**
* How to respond when hash links from one prerendered page to another don't correspond to an `id` on the destination page.
*
* - `'fail'` fail the build
* - `'fail'` - fail the build
* - `'ignore'` - silently ignore the failure and continue
* - `'warn'` continue, but print a warning
* - `(details) => void` a custom error handler that takes a `details` object with `path`, `id`, `referrers` and `message` properties. If you `throw` from this function, the build will fail
* - `'warn'` - continue, but print a warning
* - `(details) => void` - a custom error handler that takes a `details` object with `path`, `id`, `referrers` and `message` properties. If you `throw` from this function, the build will fail
*
* @default "fail"
* @since 1.15.7
Expand All @@ -592,10 +594,10 @@ export interface KitConfig {
/**
* How to respond when an entry generated by the `entries` export doesn't match the route it was generated from.
*
* - `'fail'` fail the build
* - `'fail'` - fail the build
* - `'ignore'` - silently ignore the failure and continue
* - `'warn'` continue, but print a warning
* - `(details) => void` a custom error handler that takes a `details` object with `generatedFromId`, `entry`, `matchedId` and `message` properties. If you `throw` from this function, the build will fail
* - `'warn'` - continue, but print a warning
* - `(details) => void` - a custom error handler that takes a `details` object with `generatedFromId`, `entry`, `matchedId` and `message` properties. If you `throw` from this function, the build will fail
*
* @default "fail"
* @since 1.16.0
Expand Down Expand Up @@ -685,8 +687,8 @@ export interface KitConfig {
*/
export type Handle = (input: {
event: RequestEvent;
resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}) => MaybePromise<Response>;
resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<void | Response>;
}) => MaybePromise<void | Response>;

/**
* 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.
Expand Down Expand Up @@ -785,9 +787,9 @@ export interface LoadEvent<
* }
* ```
*
* Setting the same header multiple times (even in separate `load` functions) is an error you can only set a given header once.
* Setting the same header multiple times (even in separate `load` functions) is an error - you can only set a given header once.
*
* You cannot add a `set-cookie` header with `setHeaders` use the [`cookies`](https://svelte.dev/docs/kit/@sveltejs-kit#Cookies) API in a server-only `load` function instead.
* You cannot add a `set-cookie` header with `setHeaders` - use the [`cookies`](https://svelte.dev/docs/kit/@sveltejs-kit#Cookies) API in a server-only `load` function instead.
*
* `setHeaders` has no effect when a `load` function runs in the browser.
*/
Expand All @@ -802,7 +804,7 @@ export interface LoadEvent<
/**
* This function declares that the `load` function has a _dependency_ on one or more URLs or custom identifiers, which can subsequently be used with [`invalidate()`](https://svelte.dev/docs/kit/$app-navigation#invalidate) to cause `load` to rerun.
*
* Most of the time you won't need this, as `fetch` calls `depends` on your behalf it's only necessary if you're using a custom API client that bypasses `fetch`.
* Most of the time you won't need this, as `fetch` calls `depends` on your behalf - it's only necessary if you're using a custom API client that bypasses `fetch`.
*
* URLs can be absolute or relative to the page being loaded, and must be [encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
*
Expand Down Expand Up @@ -1076,6 +1078,10 @@ export interface RequestEvent<
* The original request object
*/
request: Request;
/**
* The upgrade request object
*/
upgrade?: {request: IncomingMessage, socket: Duplex, head: Buffer}
/**
* Info about the current route
*/
Expand Down Expand Up @@ -1103,9 +1109,9 @@ export interface RequestEvent<
* }
* ```
*
* Setting the same header multiple times (even in separate `load` functions) is an error you can only set a given header once.
* Setting the same header multiple times (even in separate `load` functions) is an error - you can only set a given header once.
*
* You cannot add a `set-cookie` header with `setHeaders` use the [`cookies`](https://svelte.dev/docs/kit/@sveltejs-kit#Cookies) API instead.
* You cannot add a `set-cookie` header with `setHeaders` - use the [`cookies`](https://svelte.dev/docs/kit/@sveltejs-kit#Cookies) API instead.
*/
setHeaders(headers: Record<string, string>): void;
/**
Expand Down Expand Up @@ -1133,6 +1139,16 @@ export type RequestHandler<
RouteId extends string | null = string | null
> = (event: RequestEvent<Params, RouteId>) => MaybePromise<Response>;

/**
* 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<Record<string, string>> = Partial<Record<string, string>>,
RouteId extends string | null = string | null
> = (event: RequestEvent<Params, RouteId>) => MaybePromise<void>;

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
Expand Down Expand Up @@ -1226,7 +1242,7 @@ export interface ServerLoadEvent<
/**
* This function declares that the `load` function has a _dependency_ on one or more URLs or custom identifiers, which can subsequently be used with [`invalidate()`](https://svelte.dev/docs/kit/$app-navigation#invalidate) to cause `load` to rerun.
*
* Most of the time you won't need this, as `fetch` calls `depends` on your behalf it's only necessary if you're using a custom API client that bypasses `fetch`.
* Most of the time you won't need this, as `fetch` calls `depends` on your behalf - it's only necessary if you're using a custom API client that bypasses `fetch`.
*
* URLs can be absolute or relative to the page being loaded, and must be [encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding).
*
Expand Down
84 changes: 67 additions & 17 deletions packages/kit/src/exports/vite/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function dev(vite, vite_config, svelte_config) {
globalThis.fetch = (info, init) => {
if (typeof info === 'string' && !SCHEME.test(info)) {
throw new Error(
`Cannot use relative URL (${info}) with global fetch use \`event.fetch\` instead: https://svelte.dev/docs/kit/web-standards#fetch-apis`
`Cannot use relative URL (${info}) with global fetch - use \`event.fetch\` instead: https://svelte.dev/docs/kit/web-standards#fetch-apis`
);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -521,24 +567,28 @@ export async function dev(vite, vite_config, svelte_config) {
return;
}

const rendered = 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));
}
const rendered = 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));
return fs.readFileSync(path.join(svelte_config.kit.files.assets, file));
},
before_handle: (event, config, prerender) => {
async_local_storage.enterWith({ event, config, prerender });
},
emulator
},
before_handle: (event, config, prerender) => {
async_local_storage.enterWith({ event, config, prerender });
},
emulator
});
undefined
);

if (rendered.status === 404) {
// @ts-expect-error
Expand Down
1 change: 1 addition & 0 deletions packages/kit/src/runtime/app/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ export function read(asset) {

throw new Error(`Asset does not exist: ${file}`);
}

48 changes: 28 additions & 20 deletions packages/kit/src/runtime/server/endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ 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<Response>}
* @returns {Promise<Response | void>}
*/
export async function render_endpoint(event, mod, state) {
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
Expand Down Expand Up @@ -40,28 +40,36 @@ export async function render_endpoint(event, mod, state) {
}

try {
let response = await handler(
/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event)
);

if (!(response instanceof Response)) {
throw new Error(
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
if (method === 'GET' && event.request.headers.has('upgrade') && event.upgrade && mod.UPGRADE) {
console.log('upgrade');
await mod.UPGRADE(/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event));
} else {
console.log('not upgrade');
let response = await handler(
/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event)
);
}

if (state.prerendering) {
// the returned Response might have immutable Headers
// so we should clone them before trying to mutate them
response = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: new Headers(response.headers)
});
response.headers.set('x-sveltekit-prerender', String(prerender));
if (!(response instanceof Response)) {
throw new Error(
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
);
}

if (state.prerendering) {
// the returned Response might have immutable Headers
// so we should clone them before trying to mutate them
response = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: new Headers(response.headers)
});
response.headers.set('x-sveltekit-prerender', String(prerender));
}

console.log(response);

return response;
}

return response;
} catch (e) {
if (e instanceof Redirect) {
return new Response(undefined, {
Expand Down
5 changes: 3 additions & 2 deletions packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@ 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) {
async respond(request, options, webhookRequest) {
return respond(request, this.#options, this.#manifest, {
...options,
error: false,
depth: 0
});
}, webhookRequest);
}
}
Loading
Loading