@sveltejs/kit 1.0.0-next.295 → 1.0.0-next.298

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.
@@ -795,7 +795,7 @@ function create_client({ target, session, base, trailing_slash }) {
795
795
  * @param {import('./types').NavigationIntent} intent
796
796
  * @param {boolean} no_cache
797
797
  */
798
- async function load_route(route, { id, url, path }, no_cache) {
798
+ async function load_route(route, { id, url, path, routes }, no_cache) {
799
799
  if (!no_cache) {
800
800
  const cached = cache.get(id);
801
801
  if (cached) return cached;
@@ -804,7 +804,7 @@ function create_client({ target, session, base, trailing_slash }) {
804
804
  const [pattern, a, b, get_params, shadow_key] = route;
805
805
  const params = get_params
806
806
  ? // the pattern is for the route which we've already matched to this path
807
- get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
807
+ get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
808
808
  : {};
809
809
 
810
810
  const changed = current.url && {
@@ -876,10 +876,14 @@ function create_client({ target, session, base, trailing_slash }) {
876
876
  }
877
877
 
878
878
  if (res.status === 204) {
879
- // fallthrough
880
- return;
879
+ if (route !== routes[routes.length - 1]) {
880
+ // fallthrough
881
+ return;
882
+ }
883
+ props = {};
884
+ } else {
885
+ props = await res.json();
881
886
  }
882
- props = await res.json();
883
887
  } else {
884
888
  status = res.status;
885
889
  error = new Error('Failed to load data');
@@ -1685,7 +1685,7 @@ async function load_node({
1685
1685
 
1686
1686
  if (options.read) {
1687
1687
  const type = is_asset
1688
- ? options.manifest._.mime[filename.slice(filename.lastIndexOf('.'))]
1688
+ ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
1689
1689
  : 'text/html';
1690
1690
 
1691
1691
  response = new Response(options.read(file), {
@@ -1720,7 +1720,9 @@ async function load_node({
1720
1720
 
1721
1721
  response = await respond(new Request(new URL(requested, event.url).href, opts), options, {
1722
1722
  fetched: requested,
1723
- initiator: route
1723
+ getClientAddress: state.getClientAddress,
1724
+ initiator: route,
1725
+ prerender: state.prerender
1724
1726
  });
1725
1727
 
1726
1728
  if (state.prerender) {
@@ -2503,7 +2505,7 @@ const DATA_SUFFIX = '/__data.json';
2503
2505
  const default_transform = ({ html }) => html;
2504
2506
 
2505
2507
  /** @type {import('types').Respond} */
2506
- async function respond(request, options, state = {}) {
2508
+ async function respond(request, options, state) {
2507
2509
  const url = new URL(request.url);
2508
2510
 
2509
2511
  const normalized = normalize_path(url.pathname, options.trailing_slash);
@@ -2544,11 +2546,26 @@ async function respond(request, options, state = {}) {
2544
2546
 
2545
2547
  /** @type {import('types').RequestEvent} */
2546
2548
  const event = {
2547
- request,
2548
- url,
2549
- params: {},
2549
+ get clientAddress() {
2550
+ if (!state.getClientAddress) {
2551
+ throw new Error(
2552
+ `${
2553
+ import.meta.env.VITE_SVELTEKIT_ADAPTER_NAME
2554
+ } does not specify getClientAddress. Please raise an issue`
2555
+ );
2556
+ }
2557
+
2558
+ Object.defineProperty(event, 'clientAddress', {
2559
+ value: state.getClientAddress()
2560
+ });
2561
+
2562
+ return event.clientAddress;
2563
+ },
2550
2564
  locals: {},
2551
- platform: state.platform
2565
+ params: {},
2566
+ platform: state.platform,
2567
+ request,
2568
+ url
2552
2569
  };
2553
2570
 
2554
2571
  // TODO remove this for 1.0
@@ -2750,6 +2767,10 @@ async function respond(request, options, state = {}) {
2750
2767
  });
2751
2768
  }
2752
2769
 
2770
+ if (state.prerender) {
2771
+ return new Response('not found', { status: 404 });
2772
+ }
2773
+
2753
2774
  // we can't load the endpoint from our own manifest,
2754
2775
  // so we need to make an actual HTTP request
2755
2776
  return await fetch(request);
@@ -61,8 +61,8 @@ async function create_plugin(config, cwd) {
61
61
  manifest = {
62
62
  appDir: config.kit.appDir,
63
63
  assets: new Set(manifest_data.assets.map((asset) => asset.file)),
64
+ mimeTypes: get_mime_lookup(manifest_data),
64
65
  _: {
65
- mime: get_mime_lookup(manifest_data),
66
66
  entry: {
67
67
  file: `/@fs${runtime}/client/start.js`,
68
68
  css: [],
@@ -168,7 +168,7 @@ async function create_plugin(config, cwd) {
168
168
  update_manifest();
169
169
 
170
170
  vite.watcher.on('add', update_manifest);
171
- vite.watcher.on('remove', update_manifest);
171
+ vite.watcher.on('unlink', update_manifest);
172
172
 
173
173
  const assets = config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base;
174
174
  const asset_server = sirv(config.kit.files.assets, {
@@ -271,62 +271,72 @@ async function create_plugin(config, cwd) {
271
271
 
272
272
  const template = load_template(cwd, config);
273
273
 
274
- const rendered = await respond(request, {
275
- amp: config.kit.amp,
276
- csp: config.kit.csp,
277
- dev: true,
278
- floc: config.kit.floc,
279
- get_stack: (error) => {
280
- return fix_stack_trace(error);
281
- },
282
- handle_error: (error, event) => {
283
- hooks.handleError({
284
- error: new Proxy(error, {
285
- get: (target, property) => {
286
- if (property === 'stack') {
287
- return fix_stack_trace(error);
274
+ const rendered = await respond(
275
+ request,
276
+ {
277
+ amp: config.kit.amp,
278
+ csp: config.kit.csp,
279
+ dev: true,
280
+ floc: config.kit.floc,
281
+ get_stack: (error) => {
282
+ return fix_stack_trace(error);
283
+ },
284
+ handle_error: (error, event) => {
285
+ hooks.handleError({
286
+ error: new Proxy(error, {
287
+ get: (target, property) => {
288
+ if (property === 'stack') {
289
+ return fix_stack_trace(error);
290
+ }
291
+
292
+ return Reflect.get(target, property, target);
288
293
  }
289
-
290
- return Reflect.get(target, property, target);
294
+ }),
295
+ event,
296
+
297
+ // TODO remove for 1.0
298
+ // @ts-expect-error
299
+ get request() {
300
+ throw new Error(
301
+ 'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
302
+ );
291
303
  }
292
- }),
293
- event,
294
-
295
- // TODO remove for 1.0
296
- // @ts-expect-error
297
- get request() {
298
- throw new Error(
299
- 'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
300
- );
301
- }
302
- });
303
- },
304
- hooks,
305
- hydrate: config.kit.browser.hydrate,
306
- manifest,
307
- method_override: config.kit.methodOverride,
308
- paths: {
309
- base: config.kit.paths.base,
310
- assets
311
- },
312
- prefix: '',
313
- prerender: config.kit.prerender.enabled,
314
- read: (file) => fs__default.readFileSync(path__default.join(config.kit.files.assets, file)),
315
- root,
316
- router: config.kit.browser.router,
317
- template: ({ head, body, assets, nonce }) => {
318
- return (
319
- template
320
- .replace(/%svelte\.assets%/g, assets)
321
- .replace(/%svelte\.nonce%/g, nonce)
322
- // head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
323
- .replace('%svelte.head%', () => head)
324
- .replace('%svelte.body%', () => body)
325
- );
304
+ });
305
+ },
306
+ hooks,
307
+ hydrate: config.kit.browser.hydrate,
308
+ manifest,
309
+ method_override: config.kit.methodOverride,
310
+ paths: {
311
+ base: config.kit.paths.base,
312
+ assets
313
+ },
314
+ prefix: '',
315
+ prerender: config.kit.prerender.enabled,
316
+ read: (file) => fs__default.readFileSync(path__default.join(config.kit.files.assets, file)),
317
+ root,
318
+ router: config.kit.browser.router,
319
+ template: ({ head, body, assets, nonce }) => {
320
+ return (
321
+ template
322
+ .replace(/%svelte\.assets%/g, assets)
323
+ .replace(/%svelte\.nonce%/g, nonce)
324
+ // head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
325
+ .replace('%svelte.head%', () => head)
326
+ .replace('%svelte.body%', () => body)
327
+ );
328
+ },
329
+ template_contains_nonce: template.includes('%svelte.nonce%'),
330
+ trailing_slash: config.kit.trailingSlash
326
331
  },
327
- template_contains_nonce: template.includes('%svelte.nonce%'),
328
- trailing_slash: config.kit.trailingSlash
329
- });
332
+ {
333
+ getClientAddress: () => {
334
+ const { remoteAddress } = req.socket;
335
+ if (remoteAddress) return remoteAddress;
336
+ throw new Error('Could not determine clientAddress');
337
+ }
338
+ }
339
+ );
330
340
 
331
341
  if (rendered) {
332
342
  setResponse(res, rendered);
@@ -306,15 +306,6 @@ let read = null;
306
306
 
307
307
  set_paths(${s(config.kit.paths)});
308
308
 
309
- // this looks redundant, but the indirection allows us to access
310
- // named imports without triggering Rollup's missing import detection
311
- const get_hooks = hooks => ({
312
- getSession: hooks.getSession || (() => ({})),
313
- handle: hooks.handle || (({ event, resolve }) => resolve(event)),
314
- handleError: hooks.handleError || (({ error }) => console.error(error.stack)),
315
- externalFetch: hooks.externalFetch || fetch
316
- });
317
-
318
309
  let default_protocol = 'https';
319
310
 
320
311
  // allow paths to be globally overridden
@@ -328,8 +319,6 @@ export function override(settings) {
328
319
 
329
320
  export class Server {
330
321
  constructor(manifest) {
331
- const hooks = get_hooks(user_hooks);
332
-
333
322
  this.options = {
334
323
  amp: ${config.kit.amp},
335
324
  csp: ${s(config.kit.csp)},
@@ -337,7 +326,7 @@ export class Server {
337
326
  floc: ${config.kit.floc},
338
327
  get_stack: error => String(error), // for security
339
328
  handle_error: (error, event) => {
340
- hooks.handleError({
329
+ this.options.hooks.handleError({
341
330
  error,
342
331
  event,
343
332
 
@@ -349,7 +338,7 @@ export class Server {
349
338
  });
350
339
  error.stack = this.options.get_stack(error);
351
340
  },
352
- hooks,
341
+ hooks: null,
353
342
  hydrate: ${s(config.kit.browser.hydrate)},
354
343
  manifest,
355
344
  method_override: ${s(config.kit.methodOverride)},
@@ -366,11 +355,21 @@ export class Server {
366
355
  };
367
356
  }
368
357
 
369
- respond(request, options = {}) {
358
+ async respond(request, options = {}) {
370
359
  if (!(request instanceof Request)) {
371
360
  throw new Error('The first argument to server.respond must be a Request object. See https://github.com/sveltejs/kit/pull/3384 for details');
372
361
  }
373
362
 
363
+ if (!this.options.hooks) {
364
+ const module = await import(${s(hooks)});
365
+ this.options.hooks = {
366
+ getSession: module.getSession || (() => ({})),
367
+ handle: module.handle || (({ event, resolve }) => resolve(event)),
368
+ handleError: module.handleError || (({ error }) => console.error(error.stack)),
369
+ externalFetch: module.externalFetch || fetch
370
+ };
371
+ }
372
+
374
373
  return respond(request, this.options, options);
375
374
  }
376
375
  }
@@ -501,6 +500,8 @@ async function build_server(
501
500
 
502
501
  print_config_conflicts(conflicts, 'kit.vite.', 'build_server');
503
502
 
503
+ process.env.VITE_SVELTEKIT_ADAPTER_NAME = config.kit.adapter?.name;
504
+
504
505
  const { chunks } = await create_build(merged_config);
505
506
 
506
507
  /** @type {import('vite').Manifest} */
@@ -1074,6 +1075,7 @@ async function prerender({ config, entries, files, log }) {
1074
1075
  const dependencies = new Map();
1075
1076
 
1076
1077
  const response = await server.respond(new Request(`http://sveltekit-prerender${encoded}`), {
1078
+ getClientAddress,
1077
1079
  prerender: {
1078
1080
  default: config.kit.prerender.default,
1079
1081
  dependencies
@@ -1210,6 +1212,7 @@ async function prerender({ config, entries, files, log }) {
1210
1212
  }
1211
1213
 
1212
1214
  const rendered = await server.respond(new Request('http://sveltekit-prerender/[fallback]'), {
1215
+ getClientAddress,
1213
1216
  prerender: {
1214
1217
  fallback: true,
1215
1218
  default: false,
@@ -1224,6 +1227,11 @@ async function prerender({ config, entries, files, log }) {
1224
1227
  return prerendered;
1225
1228
  }
1226
1229
 
1230
+ /** @return {string} */
1231
+ function getClientAddress() {
1232
+ throw new Error('Cannot read clientAddress during prerendering');
1233
+ }
1234
+
1227
1235
  /**
1228
1236
  * @param {import('types').ValidatedConfig} config
1229
1237
  * @param {{ log: import('types').Logger }} opts
@@ -58,8 +58,8 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
58
58
  return `{
59
59
  appDir: ${s(build_data.app_dir)},
60
60
  assets: new Set(${s(assets)}),
61
+ mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))},
61
62
  _: {
62
- mime: ${s(get_mime_lookup(build_data.manifest_data))},
63
63
  entry: ${s(build_data.client.entry)},
64
64
  nodes: [
65
65
  ${Array.from(bundled_nodes.values()).map(node => importer(node.path)).join(',\n\t\t\t\t')}
@@ -145,7 +145,16 @@ async function preview({ port, host, config, https: use_https = false }) {
145
145
  return res.end(err.reason || 'Invalid request body');
146
146
  }
147
147
 
148
- setResponse(res, await server.respond(request));
148
+ setResponse(
149
+ res,
150
+ await server.respond(request, {
151
+ getClientAddress: () => {
152
+ const { remoteAddress } = req.socket;
153
+ if (remoteAddress) return remoteAddress;
154
+ throw new Error('Could not determine clientAddress');
155
+ }
156
+ })
157
+ );
149
158
  }
150
159
  ]);
151
160
 
@@ -895,6 +895,22 @@ function validate(config, out, user_file) {
895
895
  }
896
896
  }
897
897
 
898
+ /** @param {string} imports */
899
+ const header = (imports) => `
900
+ // this file is auto-generated
901
+ import type { ${imports} } from '@sveltejs/kit';`;
902
+
903
+ /** @param {string} arg */
904
+ const endpoint = (arg) => `
905
+ export type RequestHandler<Output extends ResponseBody = ResponseBody> = GenericRequestHandler<${arg}, Output>;`;
906
+
907
+ /** @param {string} arg */
908
+ const page = (arg) => `
909
+ export type Load<
910
+ InputProps extends Record<string, any> = Record<string, any>,
911
+ OutputProps extends Record<string, any> = InputProps
912
+ > = GenericLoad<${arg}, InputProps, OutputProps>;`;
913
+
898
914
  /**
899
915
  * @param {import('types').ValidatedConfig} config
900
916
  * @param {import('types').ManifestData} manifest_data
@@ -948,25 +964,25 @@ function write_types(config, manifest_data) {
948
964
  const arg =
949
965
  params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}';
950
966
 
951
- const imports = [
952
- type !== 'page' && 'RequestHandler as GenericRequestHandler',
953
- type !== 'endpoint' && 'Load as GenericLoad'
954
- ]
955
- .filter(Boolean)
956
- .join(', ');
957
-
958
- const file = `${config.kit.outDir}/types/${key || 'index'}.d.ts`;
959
- const content = [
960
- '// this file is auto-generated',
961
- `import type { ${imports} } from '@sveltejs/kit';`,
962
- type !== 'page' && `export type RequestHandler = GenericRequestHandler<${arg}>;`,
963
- type !== 'endpoint' &&
964
- `export type Load<Props = Record<string, any>> = GenericLoad<${arg}, Props>;`
965
- ]
966
- .filter(Boolean)
967
- .join('\n');
967
+ const imports = [];
968
+ const content = [];
969
+
970
+ if (type !== 'page') {
971
+ imports.push('RequestHandler as GenericRequestHandler, ResponseBody');
972
+ content.push(endpoint(arg));
973
+ }
968
974
 
969
- write_if_changed(file, content);
975
+ if (type !== 'endpoint') {
976
+ imports.push('Load as GenericLoad');
977
+ content.push(page(arg));
978
+ }
979
+
980
+ content.unshift(header(imports.join(', ')));
981
+
982
+ write_if_changed(
983
+ `${config.kit.outDir}/types/${key || 'index'}.d.ts`,
984
+ content.join('\n').trim()
985
+ );
970
986
  });
971
987
  }
972
988
 
package/dist/cli.js CHANGED
@@ -870,7 +870,7 @@ async function launch(port, https) {
870
870
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
871
871
  }
872
872
 
873
- const prog = sade('svelte-kit').version('1.0.0-next.295');
873
+ const prog = sade('svelte-kit').version('1.0.0-next.298');
874
874
 
875
875
  prog
876
876
  .command('dev')
@@ -1043,7 +1043,7 @@ async function check_port(port) {
1043
1043
  function welcome({ port, host, https, open, loose, allow, cwd }) {
1044
1044
  if (open) launch(port, https);
1045
1045
 
1046
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.295'}\n`));
1046
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.298'}\n`));
1047
1047
 
1048
1048
  const protocol = https ? 'https:' : 'http:';
1049
1049
  const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.295",
3
+ "version": "1.0.0-next.298",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
package/types/index.d.ts CHANGED
@@ -6,11 +6,11 @@ import './ambient';
6
6
  import { CompileOptions } from 'svelte/types/compiler/interfaces';
7
7
  import {
8
8
  AdapterEntry,
9
- Body,
10
9
  CspDirectives,
11
10
  Either,
12
11
  ErrorLoadInput,
13
12
  Fallthrough,
13
+ JSONValue,
14
14
  LoadInput,
15
15
  LoadOutput,
16
16
  Logger,
@@ -28,7 +28,7 @@ import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal';
28
28
 
29
29
  export interface Adapter {
30
30
  name: string;
31
- adapt(builder: Builder): Promise<void>;
31
+ adapt(builder: Builder): MaybePromise<void>;
32
32
  }
33
33
 
34
34
  export interface Builder {
@@ -158,7 +158,10 @@ export interface Config {
158
158
  preprocess?: any;
159
159
  }
160
160
 
161
- export interface ErrorLoad<Params = Record<string, string>, Props = Record<string, any>> {
161
+ export interface ErrorLoad<
162
+ Params extends Record<string, string> = Record<string, string>,
163
+ Props extends Record<string, any> = Record<string, any>
164
+ > {
162
165
  (input: ErrorLoadInput<Params>): MaybePromise<LoadOutput<Props>>;
163
166
  }
164
167
 
@@ -218,11 +221,14 @@ export interface Page<Params extends Record<string, string> = Record<string, str
218
221
  * Note that you can use [generated types](/docs/types#generated-types)
219
222
  * instead of manually specifying the `Params` generic argument.
220
223
  */
221
- export interface RequestHandler<Params = Record<string, string>, Output extends Body = Body> {
224
+ export interface RequestHandler<
225
+ Params extends Record<string, string> = Record<string, string>,
226
+ Output extends ResponseBody = ResponseBody
227
+ > {
222
228
  (event: RequestEvent<Params>): RequestHandlerOutput<Output>;
223
229
  }
224
230
 
225
- export type RequestHandlerOutput<Output extends Body = Body> = MaybePromise<
231
+ export type RequestHandlerOutput<Output extends ResponseBody = ResponseBody> = MaybePromise<
226
232
  Either<
227
233
  {
228
234
  status?: number;
@@ -233,17 +239,20 @@ export type RequestHandlerOutput<Output extends Body = Body> = MaybePromise<
233
239
  >
234
240
  >;
235
241
 
242
+ export type ResponseBody = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
243
+
236
244
  export class Server {
237
245
  constructor(manifest: SSRManifest);
238
- respond(request: Request, options?: RequestOptions): Promise<Response>;
246
+ respond(request: Request, options: RequestOptions): Promise<Response>;
239
247
  }
240
248
 
241
249
  export interface SSRManifest {
242
250
  appDir: string;
243
251
  assets: Set<string>;
252
+ mimeTypes: Record<string, string>;
253
+
244
254
  /** private fields */
245
255
  _: {
246
- mime: Record<string, string>;
247
256
  entry: {
248
257
  file: string;
249
258
  js: string[];
@@ -92,7 +92,7 @@ export interface Hooks {
92
92
  export class InternalServer extends Server {
93
93
  respond(
94
94
  request: Request,
95
- options?: RequestOptions & {
95
+ options: RequestOptions & {
96
96
  prerender?: PrerenderOptions;
97
97
  }
98
98
  ): Promise<Response>;
@@ -164,7 +164,7 @@ export type RecursiveRequired<T> = {
164
164
  export type RequiredResolveOptions = Required<ResolveOptions>;
165
165
 
166
166
  export interface Respond {
167
- (request: Request, options: SSROptions, state?: SSRState): Promise<Response>;
167
+ (request: Request, options: SSROptions, state: SSRState): Promise<Response>;
168
168
  }
169
169
 
170
170
  export type RouteData = PageData | EndpointData;
@@ -302,11 +302,12 @@ export interface SSRPagePart {
302
302
  export type SSRRoute = SSREndpoint | SSRPage;
303
303
 
304
304
  export interface SSRState {
305
+ fallback?: string;
305
306
  fetched?: string;
307
+ getClientAddress: () => string;
306
308
  initiator?: SSRPage | null;
307
309
  platform?: any;
308
310
  prerender?: PrerenderOptions;
309
- fallback?: string;
310
311
  }
311
312
 
312
313
  export type StrictBody = string | Uint8Array;
@@ -28,8 +28,6 @@ export interface AdapterEntry {
28
28
  }) => void;
29
29
  }
30
30
 
31
- export type Body = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
32
-
33
31
  // Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts
34
32
  //
35
33
  // MIT License
@@ -140,7 +138,8 @@ export interface CspDirectives {
140
138
 
141
139
  export type Either<T, U> = Only<T, U> | Only<U, T>;
142
140
 
143
- export interface ErrorLoadInput<Params = Record<string, string>> extends LoadInput<Params> {
141
+ export interface ErrorLoadInput<Params extends Record<string, string> = Record<string, string>>
142
+ extends LoadInput<Params> {
144
143
  status?: number;
145
144
  error?: Error;
146
145
  }
@@ -165,7 +164,10 @@ export type JSONValue =
165
164
  | JSONValue[]
166
165
  | JSONObject;
167
166
 
168
- export interface LoadInput<Params = Record<string, string>, Props = Record<string, any>> {
167
+ export interface LoadInput<
168
+ Params extends Record<string, string> = Record<string, string>,
169
+ Props extends Record<string, any> = Record<string, any>
170
+ > {
169
171
  url: URL;
170
172
  params: Params;
171
173
  props: Props;
@@ -174,7 +176,7 @@ export interface LoadInput<Params = Record<string, string>, Props = Record<strin
174
176
  stuff: Partial<App.Stuff>;
175
177
  }
176
178
 
177
- export interface LoadOutput<Props = Record<string, any>> {
179
+ export interface LoadOutput<Props extends Record<string, any> = Record<string, any>> {
178
180
  status?: number;
179
181
  error?: string | Error;
180
182
  redirect?: string;
@@ -233,15 +235,17 @@ export interface PrerenderErrorHandler {
233
235
 
234
236
  export type PrerenderOnErrorValue = 'fail' | 'continue' | PrerenderErrorHandler;
235
237
 
236
- export interface RequestEvent<Params = Record<string, string>> {
237
- request: Request;
238
- url: URL;
239
- params: Params;
238
+ export interface RequestEvent<Params extends Record<string, string> = Record<string, string>> {
239
+ clientAddress: string;
240
240
  locals: App.Locals;
241
+ params: Params;
241
242
  platform: Readonly<App.Platform>;
243
+ request: Request;
244
+ url: URL;
242
245
  }
243
246
 
244
247
  export interface RequestOptions {
248
+ getClientAddress: () => string;
245
249
  platform?: App.Platform;
246
250
  }
247
251