@sveltejs/kit 1.0.0-next.468 → 1.0.0-next.470

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.468",
3
+ "version": "1.0.0-next.470",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -10,7 +10,7 @@
10
10
  "homepage": "https://kit.svelte.dev",
11
11
  "type": "module",
12
12
  "dependencies": {
13
- "@sveltejs/vite-plugin-svelte": "^1.0.4",
13
+ "@sveltejs/vite-plugin-svelte": "^1.0.5",
14
14
  "cookie": "^0.5.0",
15
15
  "devalue": "^3.1.2",
16
16
  "kleur": "^4.1.4",
@@ -38,11 +38,11 @@
38
38
  "svelte-preprocess": "^4.10.6",
39
39
  "typescript": "^4.8.2",
40
40
  "uvu": "^0.5.3",
41
- "vite": "^3.1.0-beta.2"
41
+ "vite": "^3.1.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "svelte": "^3.44.0",
45
- "vite": "^3.1.0-beta.1"
45
+ "vite": "^3.1.0"
46
46
  },
47
47
  "bin": {
48
48
  "svelte-kit": "svelte-kit.js"
package/src/core/env.js CHANGED
@@ -24,8 +24,17 @@ export function create_static_module(id, env) {
24
24
  return GENERATED_COMMENT + declarations.join('\n\n');
25
25
  }
26
26
 
27
- /** @param {'public' | 'private'} type */
28
- export function create_dynamic_module(type) {
27
+ /**
28
+ * @param {'public' | 'private'} type
29
+ * @param {Record<string, string> | undefined} dev_values If in a development mode, values to pre-populate the module with.
30
+ */
31
+ export function create_dynamic_module(type, dev_values) {
32
+ if (dev_values) {
33
+ const objectKeys = Object.entries(dev_values).map(
34
+ ([k, v]) => `${JSON.stringify(k)}: ${JSON.stringify(v)}`
35
+ );
36
+ return `const env = {\n${objectKeys.join(',\n')}\n}\n\nexport { env }`;
37
+ }
29
38
  return `export { env } from '${runtime_base}/env-${type}.js';`;
30
39
  }
31
40
 
@@ -103,6 +103,14 @@ function create_routes_and_nodes(cwd, config, fallback) {
103
103
  * @param {import('types').RouteData | null} parent
104
104
  */
105
105
  const walk = (depth, id, segment, parent) => {
106
+ if (/\]\[/.test(id)) {
107
+ throw new Error(`Invalid route ${id} — parameters must be separated`);
108
+ }
109
+
110
+ if (count_occurrences('[', id) !== count_occurrences(']', id)) {
111
+ throw new Error(`Invalid route ${id} — brackets are unbalanced`);
112
+ }
113
+
106
114
  const { pattern, names, types } = parse_route_id(id);
107
115
 
108
116
  const segments = id.split('/');
@@ -450,3 +458,15 @@ function list_files(dir) {
450
458
 
451
459
  return files;
452
460
  }
461
+
462
+ /**
463
+ * @param {string} needle
464
+ * @param {string} haystack
465
+ */
466
+ function count_occurrences(needle, haystack) {
467
+ let count = 0;
468
+ for (let i = 0; i < haystack.length; i += 1) {
469
+ if (haystack[i] === needle) count += 1;
470
+ }
471
+ return count;
472
+ }
@@ -110,10 +110,16 @@ export class Server {
110
110
 
111
111
  if (!this.options.hooks) {
112
112
  const module = await import(${s(hooks)});
113
+
114
+ // TODO remove this for 1.0
115
+ if (module.externalFetch) {
116
+ throw new Error('externalFetch has been removed — use handleFetch instead. See https://github.com/sveltejs/kit/pull/6565 for details');
117
+ }
118
+
113
119
  this.options.hooks = {
114
120
  handle: module.handle || (({ event, resolve }) => resolve(event)),
115
121
  handleError: module.handleError || (({ error }) => console.error(error.stack)),
116
- externalFetch: module.externalFetch || fetch
122
+ handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request))
117
123
  };
118
124
  }
119
125
  }
@@ -105,7 +105,8 @@ export function get_default_build_config({ config, input, ssr, outDir }) {
105
105
  format: 'esm',
106
106
  entryFileNames: ssr ? '[name].js' : `${prefix}/[name]-[hash].js`,
107
107
  chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name]-[hash].js`,
108
- assetFileNames: `${prefix}/assets/[name]-[hash][extname]`
108
+ assetFileNames: `${prefix}/assets/[name]-[hash][extname]`,
109
+ hoistTransitiveImports: false
109
110
  },
110
111
  preserveEntrySignatures: 'strict'
111
112
  },
@@ -133,7 +134,8 @@ export function get_default_build_config({ config, input, ssr, outDir }) {
133
134
  rollupOptions: {
134
135
  output: {
135
136
  entryFileNames: `${prefix}/workers/[name]-[hash].js`,
136
- chunkFileNames: `${prefix}/workers/chunks/[name]-[hash].js`
137
+ chunkFileNames: `${prefix}/workers/chunks/[name]-[hash].js`,
138
+ hoistTransitiveImports: false
137
139
  }
138
140
  }
139
141
  }
@@ -11,7 +11,7 @@ import { load_error_page, load_template } from '../../../core/config/index.js';
11
11
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
12
12
  import * as sync from '../../../core/sync/sync.js';
13
13
  import { get_mime_lookup, runtime_base, runtime_prefix } from '../../../core/utils.js';
14
- import { get_env, prevent_illegal_vite_imports, resolve_entry } from '../utils.js';
14
+ import { prevent_illegal_vite_imports, resolve_entry } from '../utils.js';
15
15
  import { compact } from '../../../utils/array.js';
16
16
 
17
17
  // Vite doesn't expose this so we just copy the list for now
@@ -269,13 +269,6 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
269
269
  }
270
270
  });
271
271
 
272
- const { set_private_env } = await vite.ssrLoadModule(`${runtime_base}/env-private.js`);
273
- const { set_public_env } = await vite.ssrLoadModule(`${runtime_base}/env-public.js`);
274
-
275
- const env = get_env(svelte_config.kit.env, vite_config.mode);
276
- set_private_env(env.private);
277
- set_public_env(env.public);
278
-
279
272
  return () => {
280
273
  const serve_static_middleware = vite.middlewares.stack.find(
281
274
  (middleware) =>
@@ -317,6 +310,14 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
317
310
 
318
311
  const handle = user_hooks.handle || (({ event, resolve }) => resolve(event));
319
312
 
313
+ // TODO remove for 1.0
314
+ // @ts-expect-error
315
+ if (user_hooks.externalFetch) {
316
+ throw new Error(
317
+ 'externalFetch has been removed — use handleFetch instead. See https://github.com/sveltejs/kit/pull/6565 for details'
318
+ );
319
+ }
320
+
320
321
  /** @type {import('types').Hooks} */
321
322
  const hooks = {
322
323
  handle,
@@ -331,7 +332,7 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
331
332
  console.error(colors.gray(error.stack));
332
333
  }
333
334
  }),
334
- externalFetch: user_hooks.externalFetch || fetch
335
+ handleFetch: user_hooks.handleFetch || (({ request, fetch }) => fetch(request))
335
336
  };
336
337
 
337
338
  if (/** @type {any} */ (hooks).getContext) {
@@ -411,7 +412,7 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
411
412
  base: svelte_config.kit.paths.base,
412
413
  assets
413
414
  },
414
- public_env: env.public,
415
+ public_env: {},
415
416
  read: (file) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file)),
416
417
  root,
417
418
  app_template: ({ head, body, assets, nonce }) => {
@@ -299,9 +299,15 @@ function kit() {
299
299
  case '\0$env/static/public':
300
300
  return create_static_module('$env/static/public', env.public);
301
301
  case '\0$env/dynamic/private':
302
- return create_dynamic_module('private');
302
+ return create_dynamic_module(
303
+ 'private',
304
+ vite_config_env.command === 'serve' ? env.private : undefined
305
+ );
303
306
  case '\0$env/dynamic/public':
304
- return create_dynamic_module('public');
307
+ return create_dynamic_module(
308
+ 'public',
309
+ vite_config_env.command === 'serve' ? env.public : undefined
310
+ );
305
311
  }
306
312
  },
307
313
 
@@ -1,7 +1,6 @@
1
1
  import * as cookie from 'cookie';
2
2
  import * as set_cookie_parser from 'set-cookie-parser';
3
3
  import { respond } from '../index.js';
4
- import { is_root_relative, resolve } from '../../../utils/url.js';
5
4
  import { domain_matches, path_matches } from './cookie.js';
6
5
 
7
6
  /**
@@ -20,185 +19,165 @@ export function create_fetch({ event, options, state, route, prerender_default }
20
19
  const initial_cookies = cookie.parse(event.request.headers.get('cookie') || '');
21
20
 
22
21
  /** @type {import('set-cookie-parser').Cookie[]} */
23
- const cookies = [];
22
+ const set_cookies = [];
24
23
 
25
- /** @type {typeof fetch} */
26
- const fetcher = async (resource, opts = {}) => {
27
- /** @type {string} */
28
- let requested;
29
-
30
- if (typeof resource === 'string' || resource instanceof URL) {
31
- requested = resource.toString();
32
- } else {
33
- requested = resource.url;
34
-
35
- opts = {
36
- method: resource.method,
37
- headers: resource.headers,
38
- body: resource.body,
39
- mode: resource.mode,
40
- credentials: resource.credentials,
41
- cache: resource.cache,
42
- redirect: resource.redirect,
43
- referrer: resource.referrer,
44
- integrity: resource.integrity,
45
- ...opts
46
- };
47
- }
24
+ /**
25
+ * @param {URL} url
26
+ * @param {string | null} header
27
+ */
28
+ function get_cookie_header(url, header) {
29
+ /** @type {Record<string, string>} */
30
+ const new_cookies = {};
48
31
 
49
- opts.headers = new Headers(opts.headers);
50
-
51
- // merge headers from request
52
- for (const [key, value] of event.request.headers) {
53
- if (
54
- key !== 'authorization' &&
55
- key !== 'connection' &&
56
- key !== 'content-length' &&
57
- key !== 'cookie' &&
58
- key !== 'host' &&
59
- key !== 'if-none-match' &&
60
- !opts.headers.has(key)
61
- ) {
62
- opts.headers.set(key, value);
63
- }
32
+ for (const cookie of set_cookies) {
33
+ if (!domain_matches(url.hostname, cookie.domain)) continue;
34
+ if (!path_matches(url.pathname, cookie.path)) continue;
35
+
36
+ new_cookies[cookie.name] = cookie.value;
64
37
  }
65
38
 
66
- const resolved = resolve(event.url.pathname, requested.split('?')[0]).replace(/#.+$/, '');
39
+ // cookies from explicit `cookie` header take precedence over cookies previously set
40
+ // during this load with `set-cookie`, which take precedence over the cookies
41
+ // sent by the user agent
42
+ const combined_cookies = {
43
+ ...initial_cookies,
44
+ ...new_cookies,
45
+ ...cookie.parse(header ?? '')
46
+ };
47
+
48
+ return Object.entries(combined_cookies)
49
+ .map(([name, value]) => `${name}=${value}`)
50
+ .join('; ');
51
+ }
52
+
53
+ /** @type {typeof fetch} */
54
+ const fetcher = async (info, init) => {
55
+ const request = normalize_fetch_input(info, init, event.url);
67
56
 
68
- /** @type {Response} */
69
- let response;
57
+ const request_body = init?.body;
70
58
 
71
59
  /** @type {import('types').PrerenderDependency} */
72
60
  let dependency;
73
61
 
74
- // handle fetch requests for static assets. e.g. prebaked data, etc.
75
- // we need to support everything the browser's fetch supports
76
- const prefix = options.paths.assets || options.paths.base;
77
- const filename = decodeURIComponent(
78
- resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
79
- ).slice(1);
80
- const filename_html = `${filename}/index.html`; // path may also match path/index.html
62
+ const response = await options.hooks.handleFetch({
63
+ event,
64
+ request,
65
+ fetch: async (info, init) => {
66
+ const request = normalize_fetch_input(info, init, event.url);
81
67
 
82
- const is_asset = options.manifest.assets.has(filename);
83
- const is_asset_html = options.manifest.assets.has(filename_html);
68
+ const url = new URL(request.url);
84
69
 
85
- if (is_asset || is_asset_html) {
86
- const file = is_asset ? filename : filename_html;
70
+ if (url.origin !== event.url.origin) {
71
+ // allow cookie passthrough for "same-origin"
72
+ // if SvelteKit is serving my.domain.com:
73
+ // - domain.com WILL NOT receive cookies
74
+ // - my.domain.com WILL receive cookies
75
+ // - api.domain.dom WILL NOT receive cookies
76
+ // - sub.my.domain.com WILL receive cookies
77
+ // ports do not affect the resolution
78
+ // leading dot prevents mydomain.com matching domain.com
79
+ if (
80
+ `.${url.hostname}`.endsWith(`.${event.url.hostname}`) &&
81
+ request.credentials !== 'omit'
82
+ ) {
83
+ const cookie = get_cookie_header(url, request.headers.get('cookie'));
84
+ if (cookie) request.headers.set('cookie', cookie);
85
+ }
87
86
 
88
- if (options.read) {
89
- const type = is_asset
90
- ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
91
- : 'text/html';
87
+ let response = await fetch(request);
92
88
 
93
- response = new Response(options.read(file), {
94
- headers: type ? { 'content-type': type } : {}
95
- });
96
- } else {
97
- response = await fetch(`${event.url.origin}/${file}`, /** @type {RequestInit} */ (opts));
98
- }
99
- } else if (is_root_relative(resolved)) {
100
- if (opts.credentials !== 'omit') {
101
- const authorization = event.request.headers.get('authorization');
89
+ if (request.mode === 'no-cors') {
90
+ response = new Response('', {
91
+ status: response.status,
92
+ statusText: response.statusText,
93
+ headers: response.headers
94
+ });
95
+ } else {
96
+ if (url.origin !== event.url.origin) {
97
+ const acao = response.headers.get('access-control-allow-origin');
98
+ if (!acao || (acao !== event.url.origin && acao !== '*')) {
99
+ throw new Error(
100
+ `CORS error: ${
101
+ acao ? 'Incorrect' : 'No'
102
+ } 'Access-Control-Allow-Origin' header is present on the requested resource`
103
+ );
104
+ }
105
+ }
106
+ }
102
107
 
103
- // combine cookies from the initiating request with any that were
104
- // added via set-cookie
105
- const combined_cookies = { ...initial_cookies };
108
+ return response;
109
+ }
106
110
 
107
- for (const cookie of cookies) {
108
- if (!domain_matches(event.url.hostname, cookie.domain)) continue;
109
- if (!path_matches(resolved, cookie.path)) continue;
111
+ /** @type {Response} */
112
+ let response;
110
113
 
111
- combined_cookies[cookie.name] = cookie.value;
112
- }
114
+ // handle fetch requests for static assets. e.g. prebaked data, etc.
115
+ // we need to support everything the browser's fetch supports
116
+ const prefix = options.paths.assets || options.paths.base;
117
+ const decoded = decodeURIComponent(url.pathname);
118
+ const filename = (
119
+ decoded.startsWith(prefix) ? decoded.slice(prefix.length) : decoded
120
+ ).slice(1);
121
+ const filename_html = `${filename}/index.html`; // path may also match path/index.html
113
122
 
114
- const cookie = Object.entries(combined_cookies)
115
- .map(([name, value]) => `${name}=${value}`)
116
- .join('; ');
123
+ const is_asset = options.manifest.assets.has(filename);
124
+ const is_asset_html = options.manifest.assets.has(filename_html);
117
125
 
118
- if (cookie) {
119
- opts.headers.set('cookie', cookie);
120
- }
126
+ if (is_asset || is_asset_html) {
127
+ const file = is_asset ? filename : filename_html;
121
128
 
122
- if (authorization && !opts.headers.has('authorization')) {
123
- opts.headers.set('authorization', authorization);
124
- }
125
- }
129
+ if (options.read) {
130
+ const type = is_asset
131
+ ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
132
+ : 'text/html';
126
133
 
127
- if (opts.body && typeof opts.body !== 'string') {
128
- // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
129
- // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
130
- // non-string bodies are irksome to deal with, but luckily aren't particularly useful
131
- // in this context anyway, so we take the easy route and ban them
132
- throw new Error('Request body must be a string');
133
- }
134
+ return new Response(options.read(file), {
135
+ headers: type ? { 'content-type': type } : {}
136
+ });
137
+ }
134
138
 
135
- response = await respond(
136
- new Request(new URL(requested, event.url).href, { ...opts }),
137
- options,
138
- {
139
- prerender_default,
140
- ...state,
141
- initiator: route
139
+ return await fetch(request);
142
140
  }
143
- );
144
-
145
- if (state.prerendering) {
146
- dependency = { response, body: null };
147
- state.prerendering.dependencies.set(resolved, dependency);
148
- }
149
- } else {
150
- // external
151
- if (resolved.startsWith('//')) {
152
- requested = event.url.protocol + requested;
153
- }
154
141
 
155
- const url = new URL(requested);
156
-
157
- // external fetch
158
- // allow cookie passthrough for "same-origin"
159
- // if SvelteKit is serving my.domain.com:
160
- // - domain.com WILL NOT receive cookies
161
- // - my.domain.com WILL receive cookies
162
- // - api.domain.dom WILL NOT receive cookies
163
- // - sub.my.domain.com WILL receive cookies
164
- // ports do not affect the resolution
165
- // leading dot prevents mydomain.com matching domain.com
166
- if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && opts.credentials !== 'omit') {
167
- const cookie = event.request.headers.get('cookie');
168
- if (cookie) opts.headers.set('cookie', cookie);
169
- }
142
+ if (request.credentials !== 'omit') {
143
+ const cookie = get_cookie_header(url, request.headers.get('cookie'));
144
+ if (cookie) {
145
+ request.headers.set('cookie', cookie);
146
+ }
170
147
 
171
- // we need to delete the connection header, as explained here:
172
- // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467
173
- // TODO this may be a case for being selective about which headers we let through
174
- opts.headers.delete('connection');
148
+ const authorization = event.request.headers.get('authorization');
149
+ if (authorization && !request.headers.has('authorization')) {
150
+ request.headers.set('authorization', authorization);
151
+ }
152
+ }
175
153
 
176
- const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
177
- response = await options.hooks.externalFetch.call(null, external_request);
154
+ if (request_body && typeof request_body !== 'string') {
155
+ // TODO is this still necessary? we just bail out below
156
+ // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
157
+ // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
158
+ // non-string bodies are irksome to deal with, but luckily aren't particularly useful
159
+ // in this context anyway, so we take the easy route and ban them
160
+ throw new Error('Request body must be a string');
161
+ }
178
162
 
179
- if (opts.mode === 'no-cors') {
180
- response = new Response('', {
181
- status: response.status,
182
- statusText: response.statusText,
183
- headers: response.headers
163
+ response = await respond(request, options, {
164
+ prerender_default,
165
+ ...state,
166
+ initiator: route
184
167
  });
185
- } else {
186
- if (url.origin !== event.url.origin) {
187
- const acao = response.headers.get('access-control-allow-origin');
188
- if (!acao || (acao !== event.url.origin && acao !== '*')) {
189
- throw new Error(
190
- `CORS error: ${
191
- acao ? 'Incorrect' : 'No'
192
- } 'Access-Control-Allow-Origin' header is present on the requested resource`
193
- );
194
- }
168
+
169
+ if (state.prerendering) {
170
+ dependency = { response, body: null };
171
+ state.prerendering.dependencies.set(url.pathname, dependency);
195
172
  }
173
+
174
+ return response;
196
175
  }
197
- }
176
+ });
198
177
 
199
178
  const set_cookie = response.headers.get('set-cookie');
200
179
  if (set_cookie) {
201
- cookies.push(
180
+ set_cookies.push(
202
181
  ...set_cookie_parser
203
182
  .splitCookiesString(set_cookie)
204
183
  .map((str) => set_cookie_parser.parseString(str))
@@ -220,7 +199,7 @@ export function create_fetch({ event, options, state, route, prerender_default }
220
199
  }
221
200
  }
222
201
 
223
- if (!opts.body || typeof opts.body === 'string') {
202
+ if (!body || typeof body === 'string') {
224
203
  const status_number = Number(response.status);
225
204
  if (isNaN(status_number)) {
226
205
  throw new Error(
@@ -231,9 +210,11 @@ export function create_fetch({ event, options, state, route, prerender_default }
231
210
  }
232
211
 
233
212
  fetched.push({
234
- url: requested,
235
- method: opts.method || 'GET',
236
- body: opts.body,
213
+ url: request.url.startsWith(event.url.origin)
214
+ ? request.url.slice(event.url.origin.length)
215
+ : request.url,
216
+ method: request.method,
217
+ body: /** @type {string | undefined} */ (request_body),
237
218
  response: {
238
219
  status: status_number,
239
220
  statusText: response.statusText,
@@ -284,5 +265,18 @@ export function create_fetch({ event, options, state, route, prerender_default }
284
265
  return proxy;
285
266
  };
286
267
 
287
- return { fetcher, fetched, cookies };
268
+ return { fetcher, fetched, cookies: set_cookies };
269
+ }
270
+
271
+ /**
272
+ * @param {RequestInfo | URL} info
273
+ * @param {RequestInit | undefined} init
274
+ * @param {URL} url
275
+ */
276
+ function normalize_fetch_input(info, init, url) {
277
+ if (info instanceof Request) {
278
+ return info;
279
+ }
280
+
281
+ return new Request(typeof info === 'string' ? new URL(info, url) : info, init);
288
282
  }
@@ -12,14 +12,6 @@ export function parse_route_id(id) {
12
12
  // const add_trailing_slash = !/\.[a-z]+$/.test(key);
13
13
  let add_trailing_slash = true;
14
14
 
15
- if (/\]\[/.test(id)) {
16
- throw new Error(`Invalid route ${id} — parameters must be separated`);
17
- }
18
-
19
- if (count_occurrences('[', id) !== count_occurrences(']', id)) {
20
- throw new Error(`Invalid route ${id} — brackets are unbalanced`);
21
- }
22
-
23
15
  const pattern =
24
16
  id === ''
25
17
  ? /^\/$/
@@ -123,15 +115,3 @@ export function exec(match, names, types, matchers) {
123
115
 
124
116
  return params;
125
117
  }
126
-
127
- /**
128
- * @param {string} needle
129
- * @param {string} haystack
130
- */
131
- function count_occurrences(needle, haystack) {
132
- let count = 0;
133
- for (let i = 0; i < haystack.length; i += 1) {
134
- if (haystack[i] === needle) count += 1;
135
- }
136
- return count;
137
- }
package/types/index.d.ts CHANGED
@@ -176,10 +176,6 @@ export interface KitConfig {
176
176
  };
177
177
  }
178
178
 
179
- export interface ExternalFetch {
180
- (req: Request): Promise<Response>;
181
- }
182
-
183
179
  export interface Handle {
184
180
  (input: {
185
181
  event: RequestEvent;
@@ -191,6 +187,10 @@ export interface HandleError {
191
187
  (input: { error: Error & { frame?: string }; event: RequestEvent }): void;
192
188
  }
193
189
 
190
+ export interface HandleFetch {
191
+ (input: { event: RequestEvent; request: Request; fetch: typeof fetch }): MaybePromise<Response>;
192
+ }
193
+
194
194
  /**
195
195
  * The generic form of `PageLoad` and `LayoutLoad`. You should import those from `./$types` (see [generated types](https://kit.svelte.dev/docs/types#generated-types))
196
196
  * rather than using `Load` directly.
@@ -3,7 +3,6 @@ import { SvelteComponent } from 'svelte/internal';
3
3
  import {
4
4
  Action,
5
5
  Config,
6
- ExternalFetch,
7
6
  ServerLoad,
8
7
  Handle,
9
8
  HandleError,
@@ -14,7 +13,8 @@ import {
14
13
  ResolveOptions,
15
14
  Server,
16
15
  ServerInitOptions,
17
- SSRManifest
16
+ SSRManifest,
17
+ HandleFetch
18
18
  } from './index.js';
19
19
  import {
20
20
  HttpMethod,
@@ -90,9 +90,9 @@ export type CSRRoute = {
90
90
  export type GetParams = (match: RegExpExecArray) => Record<string, string>;
91
91
 
92
92
  export interface Hooks {
93
- externalFetch: ExternalFetch;
94
93
  handle: Handle;
95
94
  handleError: HandleError;
95
+ handleFetch: HandleFetch;
96
96
  }
97
97
 
98
98
  export interface ImportNode {