Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: use Request and Response objects in endpoints and hooks #3384

Merged
merged 41 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
37ad3e2
this should be an internal type
Rich-Harris Jan 17, 2022
df834ca
change app.render return type to Response
Rich-Harris Jan 17, 2022
2f4ca67
update adapters
Rich-Harris Jan 17, 2022
1cc7e35
Merge branch 'master' into streams
Rich-Harris Jan 17, 2022
cc4be79
merge master -> streams
Rich-Harris Jan 17, 2022
b7eb5b9
lint
Rich-Harris Jan 17, 2022
46b36dd
fix tests
Rich-Harris Jan 17, 2022
b3b00d8
app.render takes a Request as input
Rich-Harris Jan 17, 2022
d6fcac3
only read body once
Rich-Harris Jan 17, 2022
44c9ae2
update adapters, remove host/protocol options
Rich-Harris Jan 18, 2022
a1bccb3
lint
Rich-Harris Jan 18, 2022
3ce5ac3
remove obsolete origin test
Rich-Harris Jan 18, 2022
2fbb868
change endpoint signature
Rich-Harris Jan 18, 2022
365f65e
fix vercel adapter
Rich-Harris Jan 18, 2022
0f1e6b1
add setResponse helper
Rich-Harris Jan 18, 2022
b006980
allow returning Response or Headers from endpoints
Rich-Harris Jan 18, 2022
d4d7ac9
fixes
Rich-Harris Jan 18, 2022
fb4d608
lint
Rich-Harris Jan 18, 2022
aed1edc
update docs
Rich-Harris Jan 18, 2022
90c3090
update adapter-node docs
Rich-Harris Jan 18, 2022
c2e9399
docs
Rich-Harris Jan 18, 2022
50d2b5e
merge master -> streams
Rich-Harris Jan 18, 2022
f05e724
whoops
Rich-Harris Jan 18, 2022
62c6a77
changesets
Rich-Harris Jan 18, 2022
d216ebb
pointless commit to try and trick netlify into working
Rich-Harris Jan 18, 2022
75abe9f
update template
Rich-Harris Jan 18, 2022
8bbfc98
changeset
Rich-Harris Jan 18, 2022
865d124
work around zip-it-and-ship-it bug
Rich-Harris Jan 18, 2022
5293c71
Update .changeset/large-icons-complain.md
Rich-Harris Jan 18, 2022
501dd33
Update .changeset/mighty-pandas-search.md
Rich-Harris Jan 18, 2022
9c20e7e
Update .changeset/strong-schools-rule.md
Rich-Harris Jan 18, 2022
f3286d7
Update documentation/docs/04-hooks.md
Rich-Harris Jan 19, 2022
ca2760e
Update packages/adapter-node/README.md
Rich-Harris Jan 19, 2022
2672f50
reduce indentation
Rich-Harris Jan 19, 2022
53fb367
merge master -> streams
Rich-Harris Jan 19, 2022
9b506f1
add more types to adapters, to reflect these changes
Rich-Harris Jan 19, 2022
84f6136
Update documentation/docs/10-adapters.md
Rich-Harris Jan 19, 2022
e05cf2c
better error messages
Rich-Harris Jan 19, 2022
3abcaec
helpful errors for removed config options
Rich-Harris Jan 19, 2022
57b4670
Merge branch 'streams' of github.com:sveltejs/kit into streams
Rich-Harris Jan 19, 2022
7a5a03a
fix tests
Rich-Harris Jan 19, 2022
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
30 changes: 1 addition & 29 deletions packages/adapter-cloudflare-workers/files/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,46 +50,18 @@ async function handle(event) {

// dynamically-generated pages
try {
const rendered = await app.render({
return await app.render({
url: request.url,
rawBody: await read(request),
headers: Object.fromEntries(request.headers),
method: request.method
});

if (rendered) {
return new Response(rendered.body, {
status: rendered.status,
headers: make_headers(rendered.headers)
});
}
} catch (e) {
return new Response('Error rendering route:' + (e.message || e.toString()), { status: 500 });
}

return new Response('Not Found', {
status: 404,
statusText: 'Not Found'
});
}

/** @param {Request} request */
async function read(request) {
return new Uint8Array(await request.arrayBuffer());
}

/** @param {Record<string, string | string[]>} headers */
function make_headers(headers) {
const result = new Headers();
for (const header in headers) {
const value = headers[header];
if (typeof value === 'string') {
result.set(header, value);
continue;
}
for (const sub of value) {
result.append(header, sub);
}
}
return result;
}
29 changes: 1 addition & 28 deletions packages/adapter-cloudflare/files/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,41 +45,14 @@ export default {

// dynamically-generated pages
try {
const rendered = await app.render({
return await app.render({
url,
rawBody: new Uint8Array(await req.arrayBuffer()),
headers: Object.fromEntries(req.headers),
method: req.method
});

if (rendered) {
return new Response(rendered.body, {
status: rendered.status,
headers: make_headers(rendered.headers)
});
}
} catch (e) {
return new Response('Error rendering route: ' + (e.message || e.toString()), { status: 500 });
}

return new Response({
Rich-Harris marked this conversation as resolved.
Show resolved Hide resolved
status: 404,
statusText: 'Not Found'
});
}
};

function make_headers(headers) {
const result = new Headers();
for (const header in headers) {
const value = headers[header];
if (typeof value === 'string') {
result.set(header, value);
continue;
}
for (const sub of value) {
result.append(header, sub);
}
}
return result;
}
25 changes: 11 additions & 14 deletions packages/adapter-netlify/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,12 @@ export function init(manifest) {
rawBody
});

if (!rendered) {
return {
statusCode: 404,
body: 'Not found'
};
}

const partial_response = {
statusCode: rendered.status,
...split_headers(rendered.headers)
};

// TODO this is probably wrong now?
if (rendered.body instanceof Uint8Array) {
// Function responses should be strings (or undefined), and responses with binary
// content should be base64 encoded and set isBase64Encoded to true.
Expand All @@ -50,14 +44,14 @@ export function init(manifest) {

return {
...partial_response,
body: rendered.body
body: await rendered.text()
};
};
}

/**
* Splits headers into two categories: single value and multi value
* @param {Record<string, string | string[]>} headers
* @param {Headers} headers
* @returns {{
* headers: Record<string, string>,
* multiValueHeaders: Record<string, string[]>
Expand All @@ -70,11 +64,14 @@ function split_headers(headers) {
/** @type {Record<string, string[]>} */
const m = {};

for (const key in headers) {
const value = headers[key];
const target = Array.isArray(value) ? m : h;
target[key] = value;
}
headers.forEach((value, key) => {
if (key === 'set-cookie') {
m[key] = value.split(', ');
} else {
h[key] = value;
}
});

return {
headers: h,
multiValueHeaders: m
Expand Down
10 changes: 4 additions & 6 deletions packages/adapter-node/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { manifest } from 'MANIFEST';

__fetch_polyfill();

const app = new App(manifest);
const app = /** @type {import('@sveltejs/kit').App} */ (new App(manifest));

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Expand Down Expand Up @@ -47,15 +47,13 @@ const ssr = async (req, res) => {
const rendered = await app.render({
url: req.url,
method: req.method,
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
headers: req.headers,
rawBody: body
});

if (rendered) {
res.writeHead(rendered.status, rendered.headers);
if (rendered.body) {
res.write(rendered.body);
}
res.writeHead(rendered.status, Object.fromEntries(rendered.headers));
if (rendered.body) res.write(new Uint8Array(await rendered.arrayBuffer()));
res.end();
} else {
res.statusCode = 404;
Expand Down
16 changes: 6 additions & 10 deletions packages/adapter-vercel/files/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { manifest } from 'MANIFEST';

__fetch_polyfill();

const app = new App(manifest);
const app = /** @type {import('@sveltejs/kit').App} */ (new App(manifest));

export default async (req, res) => {
let body;
let rawBody;

try {
body = await getRawBody(req);
rawBody = await getRawBody(req);
} catch (err) {
res.statusCode = err.status || 400;
return res.end(err.reason || 'Invalid request body');
Expand All @@ -21,13 +21,9 @@ export default async (req, res) => {
url: req.url,
method: req.method,
headers: req.headers,
rawBody: body
rawBody
});

if (rendered) {
const { status, headers, body } = rendered;
return res.writeHead(status, headers).end(body);
}

return res.writeHead(404).end();
res.writeHead(rendered.status, Object.fromEntries(rendered.headers));
res.end(await rendered.arrayBuffer());
};
12 changes: 5 additions & 7 deletions packages/kit/src/core/adapt/prerender/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { pathToFileURL, URL } from 'url';
import { mkdirp } from '../../../utils/filesystem.js';
import { __fetch_polyfill } from '../../../install-fetch.js';
import { SVELTE_KIT } from '../../constants.js';
import { get_single_valued_header } from '../../../utils/http.js';
import { is_root_relative, resolve } from '../../../utils/url.js';
import { queue } from './queue.js';
import { crawl } from './crawl.js';
Expand Down Expand Up @@ -150,8 +149,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a

if (rendered) {
const response_type = Math.floor(rendered.status / 100);
const headers = rendered.headers;
const type = headers && headers['content-type'];
const type = rendered.headers.get('content-type');
const is_html = response_type === REDIRECT || type === 'text/html';

const parts = decoded_path.split('/');
Expand All @@ -162,7 +160,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
const file = `${out}${parts.join('/')}`;

if (response_type === REDIRECT) {
const location = get_single_valued_header(headers, 'location');
const location = rendered.headers.get('location');

if (location) {
mkdirp(dirname(file));
Expand All @@ -185,7 +183,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
mkdirp(dirname(file));

log.info(`${rendered.status} ${decoded_path}`);
writeFileSync(file, rendered.body || '');
writeFileSync(file, await rendered.text());
paths.push(normalize(decoded_path));
} else if (response_type !== OK) {
error({ status: rendered.status, path, referrer, referenceType: 'linked' });
Expand Down Expand Up @@ -222,7 +220,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
});

if (is_html && config.kit.prerender.crawl) {
for (const href of crawl(/** @type {string} */ (rendered.body))) {
for (const href of crawl(await rendered.text())) {
if (href.startsWith('data:') || href.startsWith('#')) continue;

const resolved = resolve(path, href);
Expand Down Expand Up @@ -283,7 +281,7 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a

const file = join(out, fallback);
mkdirp(dirname(file));
writeFileSync(file, rendered.body || '');
writeFileSync(file, await rendered.text());
}

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
export class App {
render() {
return {
return new Response('', {
status: 200,
headers: {
'content-type': 'text/html'
},
body: ''
};
}
});
}
}

Expand Down
5 changes: 3 additions & 2 deletions packages/kit/src/core/build/build_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class App {
};
}

render(request, {
async render(request, {
prerender
} = {}) {
// TODO remove this for 1.0
Expand All @@ -105,7 +105,8 @@ export class App {
: 'default_protocol'
};

return respond({ ...request, url: new URL(request.url, protocol + '://' + host) }, this.options, { prerender });
const { status, headers, body } = await respond({ ...request, url: new URL(request.url, protocol + '://' + host) }, this.options, { prerender });
return new Response(body, { status, headers });
}
}
`;
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/core/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export async function preview({
}));

if (rendered) {
res.writeHead(rendered.status, rendered.headers);
if (rendered.body) res.write(rendered.body);
res.writeHead(rendered.status, Object.fromEntries(rendered.headers));
if (rendered.body) res.write(new Uint8Array(await rendered.arrayBuffer()));
res.end();
} else {
res.statusCode = 404;
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { hash } from '../hash.js';
import { get_single_valued_header } from '../../utils/http.js';
import { coalesce_to_error } from '../../utils/error.js';

/** @type {import('@sveltejs/kit/ssr').Respond} */
/** @type {import('types/internal').Respond} */
export async function respond(incoming, options, state = {}) {
if (incoming.url.pathname !== '/' && options.trailing_slash !== 'ignore') {
const has_trailing_slash = incoming.url.pathname.endsWith('/');
Expand Down
14 changes: 0 additions & 14 deletions packages/kit/types/ambient-modules.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,20 +183,6 @@ declare module '@sveltejs/kit/node' {
export const getRawBody: GetRawBody;
}

declare module '@sveltejs/kit/ssr' {
import { IncomingRequest, Response } from '@sveltejs/kit';
// TODO import from public types, right now its heavily coupled with internal
type Options = import('@sveltejs/kit/types/internal').SSRRenderOptions;
type State = import('@sveltejs/kit/types/internal').SSRRenderState;

export interface Respond {
(incoming: IncomingRequest & { url: URL }, options: Options, state?: State): Promise<
Response | undefined
>;
}
export const respond: Respond;
}

declare module '@sveltejs/kit/install-fetch' {
import fetch, { Headers, Request, Response } from 'node-fetch';

Expand Down
5 changes: 2 additions & 3 deletions packages/kit/types/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ReadOnlyFormData, RequestHeaders } from './helper';
import { ServerResponse } from './hooks';
import { PrerenderOptions, SSRNodeLoader, SSRRoute } from './internal';

export class App {
constructor(manifest: SSRManifest);
render(incoming: IncomingRequest): Promise<ServerResponse>;
render(incoming: IncomingRequest): Promise<Response>;
}

export class InternalApp extends App {
Expand All @@ -13,7 +12,7 @@ export class InternalApp extends App {
options?: {
prerender: PrerenderOptions;
}
): Promise<ServerResponse>;
): Promise<Response>;
}

export type RawBody = null | Uint8Array;
Expand Down
10 changes: 9 additions & 1 deletion packages/kit/types/internal.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { OutputAsset, OutputChunk } from 'rollup';
import { RequestHandler } from './endpoint';
import { InternalApp, SSRManifest } from './app';
import { IncomingRequest, InternalApp, SSRManifest } from './app';
import {
ExternalFetch,
GetSession,
Expand Down Expand Up @@ -235,3 +235,11 @@ export interface MethodOverride {
parameter: string;
allowed: string[];
}

export interface Respond {
(
incoming: IncomingRequest & { url: URL },
options: SSRRenderOptions,
state?: SSRRenderState
): Promise<ServerResponse | undefined>;
}