From bc4d7e2e9b3802414a893d3683dc9136ff31f931 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 20:01:43 -0500 Subject: [PATCH 1/6] feat: read assets inside an edge function --- packages/adapter-vercel/files/edge.js | 29 ++++++++++++++++++- packages/adapter-vercel/index.js | 13 +-------- packages/kit/types/index.d.ts | 2 +- .../src/routes/delete-me/+server.js | 12 ++++++++ .../src/routes/delete-me/file.txt | 1 + 5 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 sites/kit.svelte.dev/src/routes/delete-me/+server.js create mode 100644 sites/kit.svelte.dev/src/routes/delete-me/file.txt diff --git a/packages/adapter-vercel/files/edge.js b/packages/adapter-vercel/files/edge.js index ce603b6e3920..728637e60697 100644 --- a/packages/adapter-vercel/files/edge.js +++ b/packages/adapter-vercel/files/edge.js @@ -2,8 +2,35 @@ import { Server } from 'SERVER'; import { manifest } from 'MANIFEST'; const server = new Server(manifest); + const initialized = server.init({ - env: /** @type {Record} */ (process.env) + env: /** @type {Record} */ (process.env), + read: (file) => { + const controller = new AbortController(); + const signal = controller.signal; + + return new ReadableStream({ + async start(controller) { + try { + const response = await fetch(manifest.appPath + file, { signal }); + const reader = /** @type {ReadableStream} */ (response.body).getReader(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + controller.enqueue(value); + } + + controller.close(); + } catch (error) { + controller.error(error); + } + }, + cancel(reason) { + controller.abort(reason); + } + }); + } }); /** diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 683df1757073..400e8b73c5ea 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -341,18 +341,7 @@ const plugin = function (defaults = {}) { }, supports: { - // reading from the filesystem only works in serverless functions - read: ({ config, route }) => { - const runtime = config.runtime ?? defaults.runtime; - - if (runtime === 'edge') { - throw new Error( - `${name}: Cannot use \`read\` from \`$app/server\` in route \`${route.id}\` configured with \`runtime: 'edge'\`` - ); - } - - return true; - } + read: () => true } }; }; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 231e2ea8ee23..86cd16961983 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1145,7 +1145,7 @@ declare module '@sveltejs/kit' { /** A map of environment variables */ env: Record; /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ - read?: (file: string) => ReadableStream; + read?: (file: string) => MaybePromise; } export interface SSRManifest { diff --git a/sites/kit.svelte.dev/src/routes/delete-me/+server.js b/sites/kit.svelte.dev/src/routes/delete-me/+server.js new file mode 100644 index 000000000000..ec2f44ebc9aa --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/delete-me/+server.js @@ -0,0 +1,12 @@ +import { read } from '$app/server'; +import file from './file.txt?url'; + +export const config = { + runtime: 'edge' +}; + +export const prerender = false; + +export function GET() { + return read(file); +} diff --git a/sites/kit.svelte.dev/src/routes/delete-me/file.txt b/sites/kit.svelte.dev/src/routes/delete-me/file.txt new file mode 100644 index 000000000000..f15a5977eef6 --- /dev/null +++ b/sites/kit.svelte.dev/src/routes/delete-me/file.txt @@ -0,0 +1 @@ +Hello! This file was read inside an edge function From e7046f66e2ec6403e56583946746c98c4b087a6d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 20:02:24 -0500 Subject: [PATCH 2/6] regenerate types --- packages/kit/types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 86cd16961983..231e2ea8ee23 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1145,7 +1145,7 @@ declare module '@sveltejs/kit' { /** A map of environment variables */ env: Record; /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ - read?: (file: string) => MaybePromise; + read?: (file: string) => ReadableStream; } export interface SSRManifest { From 966b69b89e8dc4052b5a255d93e036f210c04181 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 20:05:42 -0500 Subject: [PATCH 3/6] double check... --- sites/kit.svelte.dev/src/routes/delete-me/+server.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sites/kit.svelte.dev/src/routes/delete-me/+server.js b/sites/kit.svelte.dev/src/routes/delete-me/+server.js index ec2f44ebc9aa..30637fb9f6ed 100644 --- a/sites/kit.svelte.dev/src/routes/delete-me/+server.js +++ b/sites/kit.svelte.dev/src/routes/delete-me/+server.js @@ -7,6 +7,8 @@ export const config = { export const prerender = false; -export function GET() { - return read(file); +export async function GET() { + const text = await read(file).text(); + + return `${text} at ${new Date()}`; } From a17ffa5db4f0bf6928144366a7f46258cb0223da Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 20:10:37 -0500 Subject: [PATCH 4/6] oops --- sites/kit.svelte.dev/src/routes/delete-me/+server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/kit.svelte.dev/src/routes/delete-me/+server.js b/sites/kit.svelte.dev/src/routes/delete-me/+server.js index 30637fb9f6ed..1bc58881622f 100644 --- a/sites/kit.svelte.dev/src/routes/delete-me/+server.js +++ b/sites/kit.svelte.dev/src/routes/delete-me/+server.js @@ -10,5 +10,5 @@ export const prerender = false; export async function GET() { const text = await read(file).text(); - return `${text} at ${new Date()}`; + return new Response(`${text} at ${new Date()}`); } From 59180d616d501bb81fddb0f55fd1e5aab78036cd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 18 Jan 2024 23:12:19 -0500 Subject: [PATCH 5/6] still not actually working, but closer --- packages/adapter-vercel/files/edge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-vercel/files/edge.js b/packages/adapter-vercel/files/edge.js index 728637e60697..1c04e1827a19 100644 --- a/packages/adapter-vercel/files/edge.js +++ b/packages/adapter-vercel/files/edge.js @@ -12,7 +12,7 @@ const initialized = server.init({ return new ReadableStream({ async start(controller) { try { - const response = await fetch(manifest.appPath + file, { signal }); + const response = await fetch(`https://${process.env.VERCEL_URL}/${file}`, { signal }); const reader = /** @type {ReadableStream} */ (response.body).getReader(); while (true) { From 46f23fda62cc7e6cdae6d33c7e1c9d6847791d50 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 22 Jan 2024 08:32:47 -0500 Subject: [PATCH 6/6] try this --- packages/adapter-vercel/files/edge.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/adapter-vercel/files/edge.js b/packages/adapter-vercel/files/edge.js index 1c04e1827a19..060f05b8ba6d 100644 --- a/packages/adapter-vercel/files/edge.js +++ b/packages/adapter-vercel/files/edge.js @@ -3,6 +3,13 @@ import { manifest } from 'MANIFEST'; const server = new Server(manifest); +/** + * We don't know the origin until we receive a request, but + * that's guaranteed to happen before we call `read` + * @type {string} + */ +let origin; + const initialized = server.init({ env: /** @type {Record} */ (process.env), read: (file) => { @@ -12,7 +19,7 @@ const initialized = server.init({ return new ReadableStream({ async start(controller) { try { - const response = await fetch(`https://${process.env.VERCEL_URL}/${file}`, { signal }); + const response = await fetch(`${origin}/${file}`, { signal }); const reader = /** @type {ReadableStream} */ (response.body).getReader(); while (true) { @@ -38,7 +45,11 @@ const initialized = server.init({ * @param {import('../index.js').RequestContext} context */ export default async (request, context) => { - await initialized; + if (!origin) { + origin = new URL(request.url).origin; + await initialized; + } + return server.respond(request, { getClientAddress() { return /** @type {string} */ (request.headers.get('x-forwarded-for'));