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

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.
@@ -15,6 +15,7 @@ import 'sade';
15
15
  import 'child_process';
16
16
  import 'net';
17
17
  import 'os';
18
+ import './routing.js';
18
19
  import 'node:http';
19
20
  import 'node:https';
20
21
  import 'node:zlib';
@@ -145,7 +146,6 @@ async function create_build(config) {
145
146
  * @param {import('vite').Manifest} manifest
146
147
  * @param {Set<string>} css
147
148
  * @param {Set<string>} js
148
- * @returns
149
149
  */
150
150
  function find_deps(file, manifest, js, css) {
151
151
  const chunk = manifest[file];
@@ -287,7 +287,6 @@ async function build_client({
287
287
  * runtime: string;
288
288
  * template: string;
289
289
  * }} opts
290
- * @returns
291
290
  */
292
291
  const server_template = ({ config, hooks, has_service_worker, runtime, template }) => `
293
292
  import root from '__GENERATED__/root.svelte';
@@ -424,7 +423,7 @@ async function build_server(
424
423
  }
425
424
  });
426
425
 
427
- // ...and every component used by pages
426
+ // ...and every component used by pages...
428
427
  manifest_data.components.forEach((file) => {
429
428
  const resolved = path__default.resolve(cwd, file);
430
429
  const relative = path__default.relative(config.kit.files.routes, resolved);
@@ -435,6 +434,12 @@ async function build_server(
435
434
  input[name] = resolved;
436
435
  });
437
436
 
437
+ // ...and every validator
438
+ Object.entries(manifest_data.validators).forEach(([key, file]) => {
439
+ const name = posixify(path__default.join('entries/validators', key));
440
+ input[name] = path__default.resolve(cwd, file);
441
+ });
442
+
438
443
  /** @type {(file: string) => string} */
439
444
  const app_relative = (file) => {
440
445
  const relative_file = path__default.relative(build_dir, path__default.resolve(cwd, file));
@@ -577,7 +582,6 @@ const method_names = {
577
582
  };
578
583
 
579
584
  /**
580
- *
581
585
  * @param {string} cwd
582
586
  * @param {import('rollup').OutputChunk[]} output
583
587
  * @param {import('types').ManifestData} manifest_data
@@ -1,4 +1,5 @@
1
1
  import { s } from './misc.js';
2
+ import { p as parse_route_id } from './routing.js';
2
3
  import { a as get_mime_lookup } from '../cli.js';
3
4
 
4
5
  /**
@@ -41,10 +42,13 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
41
42
  });
42
43
 
43
44
  /** @type {(path: string) => string} */
44
- const importer =
45
+ const load =
45
46
  format === 'esm'
46
- ? (path) => `() => import('${path}')`
47
- : (path) => `() => Promise.resolve().then(() => require('${path}'))`;
47
+ ? (path) => `import('${path}')`
48
+ : (path) => `Promise.resolve().then(() => require('${path}'))`;
49
+
50
+ /** @type {(path: string) => string} */
51
+ const loader = (path) => `() => ${load(path)}`;
48
52
 
49
53
  const assets = build_data.manifest_data.assets.map((asset) => asset.file);
50
54
  if (build_data.service_worker) {
@@ -54,6 +58,8 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
54
58
  /** @param {string} id */
55
59
  const get_index = (id) => id && /** @type {LookupEntry} */ (bundled_nodes.get(id)).index;
56
60
 
61
+ const validators = new Set();
62
+
57
63
  // prettier-ignore
58
64
  return `{
59
65
  appDir: ${s(build_data.app_dir)},
@@ -62,18 +68,25 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
62
68
  _: {
63
69
  entry: ${s(build_data.client.entry)},
64
70
  nodes: [
65
- ${Array.from(bundled_nodes.values()).map(node => importer(node.path)).join(',\n\t\t\t\t')}
71
+ ${Array.from(bundled_nodes.values()).map(node => loader(node.path)).join(',\n\t\t\t\t')}
66
72
  ],
67
73
  routes: [
68
74
  ${routes.map(route => {
75
+ const { pattern, names, types } = parse_route_id(route.id);
76
+
77
+ types.forEach(type => {
78
+ if (type) validators.add(type);
79
+ });
80
+
69
81
  if (route.type === 'page') {
70
82
  return `{
71
83
  type: 'page',
72
- key: ${s(route.key)},
73
- pattern: ${route.pattern},
74
- params: ${get_params(route.params)},
84
+ id: ${s(route.id)},
85
+ pattern: ${pattern},
86
+ names: ${s(names)},
87
+ types: ${s(types)},
75
88
  path: ${route.path ? s(route.path) : null},
76
- shadow: ${route.shadow ? importer(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null},
89
+ shadow: ${route.shadow ? loader(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null},
77
90
  a: ${s(route.a.map(get_index))},
78
91
  b: ${s(route.b.map(get_index))}
79
92
  }`.replace(/^\t\t/gm, '');
@@ -86,33 +99,21 @@ function generate_manifest({ build_data, relative_path, routes, format = 'esm' }
86
99
 
87
100
  return `{
88
101
  type: 'endpoint',
89
- pattern: ${route.pattern},
90
- params: ${get_params(route.params)},
91
- load: ${importer(`${relative_path}/${build_data.server.vite_manifest[route.file].file}`)}
102
+ id: ${s(route.id)},
103
+ pattern: ${pattern},
104
+ names: ${s(names)},
105
+ types: ${s(types)},
106
+ load: ${loader(`${relative_path}/${build_data.server.vite_manifest[route.file].file}`)}
92
107
  }`.replace(/^\t\t/gm, '');
93
108
  }
94
109
  }).filter(Boolean).join(',\n\t\t\t\t')}
95
- ]
110
+ ],
111
+ validators: async () => {
112
+ ${Array.from(validators).map(type => `const { validate: ${type} } = await ${load(`${relative_path}/entries/validators/${type}.js`)}`).join('\n\t\t\t\t')}
113
+ return { ${Array.from(validators).join(', ')} };
114
+ }
96
115
  }
97
116
  }`.replace(/^\t/gm, '');
98
117
  }
99
118
 
100
- /** @param {string[]} array */
101
- function get_params(array) {
102
- // given an array of params like `['x', 'y', 'z']` for
103
- // src/routes/[x]/[y]/[z]/svelte, create a function
104
- // that turns a RexExpMatchArray into ({ x, y, z })
105
- return array.length
106
- ? '(m) => ({ ' +
107
- array
108
- .map((param, i) => {
109
- return param.startsWith('...')
110
- ? `${param.slice(3)}: m[${i + 1}] || ''`
111
- : `${param}: m[${i + 1}]`;
112
- })
113
- .join(', ') +
114
- '})'
115
- : 'null';
116
- }
117
-
118
119
  export { generate_manifest as g };
@@ -9,6 +9,7 @@ import 'fs';
9
9
  import 'url';
10
10
  import 'os';
11
11
  import './misc.js';
12
+ import './routing.js';
12
13
 
13
14
  /**
14
15
  * @param {{
@@ -0,0 +1,39 @@
1
+ /** @param {string} key */
2
+ function parse_route_id(key) {
3
+ /** @type {string[]} */
4
+ const names = [];
5
+
6
+ /** @type {string[]} */
7
+ const types = [];
8
+
9
+ const pattern =
10
+ key === ''
11
+ ? /^\/$/
12
+ : new RegExp(
13
+ `^${decodeURIComponent(key)
14
+ .split('/')
15
+ .map((segment) => {
16
+ // special case — /[...rest]/ could contain zero segments
17
+ const match = /^\[\.\.\.(\w+)(?:=\w+)?\]$/.exec(segment);
18
+ if (match) {
19
+ names.push(match[1]);
20
+ types.push(match[2]);
21
+ return '(?:/(.*))?';
22
+ }
23
+
24
+ return (
25
+ '/' +
26
+ segment.replace(/\[(\.\.\.)?(\w+)(?:=(\w+))?\]/g, (m, rest, name, type) => {
27
+ names.push(name);
28
+ types.push(type);
29
+ return rest ? '(.*?)' : '([^/]+?)';
30
+ })
31
+ );
32
+ })
33
+ .join('')}/?$`
34
+ );
35
+
36
+ return { pattern, names, types };
37
+ }
38
+
39
+ export { parse_route_id as p };
@@ -119,6 +119,7 @@ var mime = new Mime(standard, other);
119
119
  * content: string;
120
120
  * dynamic: boolean;
121
121
  * rest: boolean;
122
+ * type: string | null;
122
123
  * }} Part
123
124
  * @typedef {{
124
125
  * basename: string;
@@ -168,13 +169,13 @@ function create_manifest_data({
168
169
 
169
170
  /**
170
171
  * @param {string} dir
171
- * @param {string[]} parent_key
172
+ * @param {string[]} parent_id
172
173
  * @param {Part[][]} parent_segments
173
174
  * @param {string[]} parent_params
174
175
  * @param {Array<string|undefined>} layout_stack // accumulated __layout.svelte components
175
176
  * @param {Array<string|undefined>} error_stack // accumulated __error.svelte components
176
177
  */
177
- function walk(dir, parent_key, parent_segments, parent_params, layout_stack, error_stack) {
178
+ function walk(dir, parent_id, parent_segments, parent_params, layout_stack, error_stack) {
178
179
  /** @type {Item[]} */
179
180
  let items = [];
180
181
  fs__default.readdirSync(dir).forEach((basename) => {
@@ -242,7 +243,7 @@ function create_manifest_data({
242
243
  items = items.sort(comparator);
243
244
 
244
245
  items.forEach((item) => {
245
- const key = parent_key.slice();
246
+ const id = parent_id.slice();
246
247
  const segments = parent_segments.slice();
247
248
 
248
249
  if (item.is_index) {
@@ -255,24 +256,26 @@ function create_manifest_data({
255
256
  last_segment.push({
256
257
  dynamic: false,
257
258
  rest: false,
258
- content: item.route_suffix
259
+ content: item.route_suffix,
260
+ type: null
259
261
  });
260
262
  } else {
261
263
  last_segment[last_segment.length - 1] = {
262
264
  dynamic: false,
263
265
  rest: false,
264
- content: `${last_part.content}${item.route_suffix}`
266
+ content: `${last_part.content}${item.route_suffix}`,
267
+ type: null
265
268
  };
266
269
  }
267
270
 
268
271
  segments[segments.length - 1] = last_segment;
269
- key[key.length - 1] += item.route_suffix;
272
+ id[id.length - 1] += item.route_suffix;
270
273
  } else {
271
274
  segments.push(item.parts);
272
275
  }
273
276
  }
274
277
  } else {
275
- key.push(item.name);
278
+ id.push(item.name);
276
279
  segments.push(item.parts);
277
280
  }
278
281
 
@@ -306,7 +309,7 @@ function create_manifest_data({
306
309
 
307
310
  walk(
308
311
  path__default.join(dir, item.basename),
309
- key,
312
+ id,
310
313
  segments,
311
314
  params,
312
315
  layout_reset ? [layout_reset] : layout_stack.concat(layout),
@@ -341,7 +344,7 @@ function create_manifest_data({
341
344
 
342
345
  routes.push({
343
346
  type: 'page',
344
- key: key.join('/'),
347
+ id: id.join('/'),
345
348
  segments: simple_segments,
346
349
  pattern,
347
350
  params,
@@ -355,7 +358,7 @@ function create_manifest_data({
355
358
 
356
359
  routes.push({
357
360
  type: 'endpoint',
358
- key: key.join('/'),
361
+ id: id.join('/'),
359
362
  segments: simple_segments,
360
363
  pattern,
361
364
  file: item.file,
@@ -365,10 +368,10 @@ function create_manifest_data({
365
368
  });
366
369
  }
367
370
 
368
- const base = path__default.relative(cwd, config.kit.files.routes);
371
+ const routes_base = path__default.relative(cwd, config.kit.files.routes);
369
372
 
370
- const layout = find_layout('__layout', base) || default_layout;
371
- const error = find_layout('__error', base) || default_error;
373
+ const layout = find_layout('__layout', routes_base) || default_layout;
374
+ const error = find_layout('__error', routes_base) || default_error;
372
375
 
373
376
  components.push(layout, error);
374
377
 
@@ -377,15 +380,15 @@ function create_manifest_data({
377
380
  const lookup = new Map();
378
381
  for (const route of routes) {
379
382
  if (route.type === 'page') {
380
- lookup.set(route.key, route);
383
+ lookup.set(route.id, route);
381
384
  }
382
385
  }
383
386
 
384
387
  let i = routes.length;
385
388
  while (i--) {
386
389
  const route = routes[i];
387
- if (route.type === 'endpoint' && lookup.has(route.key)) {
388
- lookup.get(route.key).shadow = route.file;
390
+ if (route.type === 'endpoint' && lookup.has(route.id)) {
391
+ lookup.get(route.id).shadow = route.file;
389
392
  routes.splice(i, 1);
390
393
  }
391
394
  }
@@ -394,12 +397,32 @@ function create_manifest_data({
394
397
  ? list_files({ config, dir: config.kit.files.assets, path: '' })
395
398
  : [];
396
399
 
400
+ const params_base = path__default.relative(cwd, config.kit.files.params);
401
+
402
+ /** @type {Record<string, string>} */
403
+ const validators = {};
404
+ if (fs__default.existsSync(config.kit.files.params)) {
405
+ for (const file of fs__default.readdirSync(config.kit.files.params)) {
406
+ const ext = path__default.extname(file);
407
+ const type = file.slice(0, -ext.length);
408
+
409
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(type)) {
410
+ validators[type] = path__default.join(params_base, file);
411
+ } else {
412
+ throw new Error(
413
+ `Validator names must match /^[a-zA-Z_][a-zA-Z0-9_]*$/ — "${file}" is invalid`
414
+ );
415
+ }
416
+ }
417
+ }
418
+
397
419
  return {
398
420
  assets,
399
421
  layout,
400
422
  error,
401
423
  components,
402
- routes
424
+ routes,
425
+ validators
403
426
  };
404
427
  }
405
428
 
@@ -456,6 +479,10 @@ function comparator(a, b) {
456
479
  return a_sub_part.dynamic ? 1 : -1;
457
480
  }
458
481
 
482
+ if (a_sub_part.dynamic && !!a_sub_part.type !== !!b_sub_part.type) {
483
+ return a_sub_part.type ? -1 : 1;
484
+ }
485
+
459
486
  if (!a_sub_part.dynamic && a_sub_part.content !== b_sub_part.content) {
460
487
  return (
461
488
  b_sub_part.content.length - a_sub_part.content.length ||
@@ -483,16 +510,25 @@ function get_parts(part, file) {
483
510
  if (!str) return;
484
511
  const dynamic = i % 2 === 1;
485
512
 
486
- const [, content] = dynamic ? /([^(]+)$/.exec(str) || [null, null] : [null, str];
487
-
488
- if (!content || (dynamic && !/^(\.\.\.)?[a-zA-Z0-9_$]+$/.test(content))) {
489
- throw new Error(`Invalid route ${file} — parameter name must match /^[a-zA-Z0-9_$]+$/`);
513
+ const [, content, type] = dynamic
514
+ ? /^((?:\.\.\.)?[a-zA-Z_][a-zA-Z0-9_]*)(?:=([a-zA-Z_][a-zA-Z0-9_]*))?$/.exec(str) || [
515
+ null,
516
+ null,
517
+ null
518
+ ]
519
+ : [null, str, null];
520
+
521
+ if (!content) {
522
+ throw new Error(
523
+ `Invalid route ${file} — parameter name and type must match /^[a-zA-Z_][a-zA-Z0-9_]*$/`
524
+ );
490
525
  }
491
526
 
492
527
  result.push({
493
528
  content,
494
529
  dynamic,
495
- rest: dynamic && /^\.{3}.+$/.test(content)
530
+ rest: dynamic && /^\.{3}.+$/.test(content),
531
+ type
496
532
  });
497
533
  });
498
534
 
@@ -640,49 +676,30 @@ function write_manifest(manifest_data, base, output) {
640
676
 
641
677
  /** @param {string[]} parts */
642
678
  const get_indices = (parts) =>
643
- `[${parts.map((part) => (part ? `c[${component_indexes[part]}]` : '')).join(', ')}]`;
679
+ `[${parts.map((part) => (part ? component_indexes[part] : '')).join(', ')}]`;
644
680
 
645
- const routes = `[
681
+ const dictionary = `{
646
682
  ${manifest_data.routes
647
683
  .map((route) => {
648
684
  if (route.type === 'page') {
649
- const params =
650
- route.params.length > 0 &&
651
- '(m) => ({ ' +
652
- route.params
653
- .map((param, i) => {
654
- return param.startsWith('...')
655
- ? `${param.slice(3)}: d(m[${i + 1}] || '')`
656
- : `${param}: d(m[${i + 1}])`;
657
- })
658
- .join(', ') +
659
- '})';
660
-
661
- const tuple = [route.pattern, get_indices(route.a), get_indices(route.b)];
662
-
663
- // optional items
664
- if (params || route.shadow) tuple.push(params || 'null');
665
- if (route.shadow) tuple.push(`'${route.key}'`);
666
-
667
- return `// ${route.a[route.a.length - 1]}\n\t\t[${tuple.join(', ')}]`;
685
+ const tuple = [get_indices(route.a), get_indices(route.b)];
686
+ if (route.shadow) tuple.push('1');
687
+
688
+ return `${s(route.id)}: [${tuple.join(', ')}]`;
668
689
  }
669
690
  })
670
691
  .filter(Boolean)
671
- .join(',\n\n\t\t')}
672
- ]`.replace(/^\t/gm, '');
692
+ .join(',\n\t\t')}
693
+ }`.replace(/^\t/gm, '');
673
694
 
674
695
  write_if_changed(
675
- `${output}/manifest.js`,
696
+ `${output}/client-manifest.js`,
676
697
  trim(`
677
- const c = ${components};
678
-
679
- const d = decodeURIComponent;
698
+ export { validators } from './client-validators.js';
680
699
 
681
- export const routes = ${routes};
700
+ export const components = ${components};
682
701
 
683
- // we import the root layout/error components eagerly, so that
684
- // connectivity errors after initialisation don't nuke the app
685
- export const fallback = [c[0](), c[1]()];
702
+ export const dictionary = ${dictionary};
686
703
  `)
687
704
  );
688
705
  }
@@ -986,6 +1003,28 @@ function write_types(config, manifest_data) {
986
1003
  });
987
1004
  }
988
1005
 
1006
+ /**
1007
+ * @param {import('types').ManifestData} manifest_data
1008
+ * @param {string} output
1009
+ */
1010
+ function write_validators(manifest_data, output) {
1011
+ const imports = [];
1012
+ const validators = [];
1013
+
1014
+ for (const key in manifest_data.validators) {
1015
+ const src = manifest_data.validators[key];
1016
+
1017
+ imports.push(`import { validate as ${key} } from ${s(path__default.relative(output, src))};`);
1018
+ validators.push(key);
1019
+ }
1020
+
1021
+ const module = imports.length
1022
+ ? `${imports.join('\n')}\n\nexport const validators = { ${validators.join(', ')} };`
1023
+ : 'export const validators = {};';
1024
+
1025
+ write_if_changed(`${output}/client-validators.js`, module);
1026
+ }
1027
+
989
1028
  /** @param {import('types').ValidatedConfig} config */
990
1029
  function init(config) {
991
1030
  copy_assets(path__default.join(config.kit.outDir, 'runtime'));
@@ -1001,6 +1040,7 @@ function update(config) {
1001
1040
 
1002
1041
  write_manifest(manifest_data, base, output);
1003
1042
  write_root(manifest_data, output);
1043
+ write_validators(manifest_data, output);
1004
1044
  write_types(config, manifest_data);
1005
1045
 
1006
1046
  return { manifest_data };
package/dist/cli.js CHANGED
@@ -403,6 +403,7 @@ const options = object(
403
403
  assets: string('static'),
404
404
  hooks: string(join('src', 'hooks')),
405
405
  lib: string(join('src', 'lib')),
406
+ params: string(join('src', 'params')),
406
407
  routes: string(join('src', 'routes')),
407
408
  serviceWorker: string(join('src', 'service-worker')),
408
409
  template: string(join('src', 'app.html'))
@@ -787,12 +788,10 @@ async function load_config({ cwd = process.cwd() } = {}) {
787
788
 
788
789
  validated.kit.outDir = path__default.resolve(cwd, validated.kit.outDir);
789
790
 
790
- validated.kit.files.assets = path__default.resolve(cwd, validated.kit.files.assets);
791
- validated.kit.files.hooks = path__default.resolve(cwd, validated.kit.files.hooks);
792
- validated.kit.files.lib = path__default.resolve(cwd, validated.kit.files.lib);
793
- validated.kit.files.routes = path__default.resolve(cwd, validated.kit.files.routes);
794
- validated.kit.files.serviceWorker = path__default.resolve(cwd, validated.kit.files.serviceWorker);
795
- validated.kit.files.template = path__default.resolve(cwd, validated.kit.files.template);
791
+ for (const key in validated.kit.files) {
792
+ // @ts-expect-error this is typescript at its stupidest
793
+ validated.kit.files[key] = path__default.resolve(cwd, validated.kit.files[key]);
794
+ }
796
795
 
797
796
  return validated;
798
797
  }
@@ -870,7 +869,7 @@ async function launch(port, https) {
870
869
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
871
870
  }
872
871
 
873
- const prog = sade('svelte-kit').version('1.0.0-next.298');
872
+ const prog = sade('svelte-kit').version('1.0.0-next.299');
874
873
 
875
874
  prog
876
875
  .command('dev')
@@ -1043,7 +1042,7 @@ async function check_port(port) {
1043
1042
  function welcome({ port, host, https, open, loose, allow, cwd }) {
1044
1043
  if (open) launch(port, https);
1045
1044
 
1046
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.298'}\n`));
1045
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.299'}\n`));
1047
1046
 
1048
1047
  const protocol = https ? 'https:' : 'http:';
1049
1048
  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.298",
3
+ "version": "1.0.0-next.299",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
package/types/index.d.ts CHANGED
@@ -7,9 +7,7 @@ import { CompileOptions } from 'svelte/types/compiler/interfaces';
7
7
  import {
8
8
  AdapterEntry,
9
9
  CspDirectives,
10
- Either,
11
10
  ErrorLoadInput,
12
- Fallthrough,
13
11
  JSONValue,
14
12
  LoadInput,
15
13
  LoadOutput,
@@ -114,6 +112,7 @@ export interface Config {
114
112
  assets?: string;
115
113
  hooks?: string;
116
114
  lib?: string;
115
+ params?: string;
117
116
  routes?: string;
118
117
  serviceWorker?: string;
119
118
  template?: string;
@@ -194,9 +193,7 @@ export interface Load<
194
193
  InputProps extends Record<string, any> = Record<string, any>,
195
194
  OutputProps extends Record<string, any> = InputProps
196
195
  > {
197
- (input: LoadInput<Params, InputProps>): MaybePromise<
198
- Either<Fallthrough, LoadOutput<OutputProps>>
199
- >;
196
+ (input: LoadInput<Params, InputProps>): MaybePromise<LoadOutput<OutputProps>>;
200
197
  }
201
198
 
202
199
  export interface Navigation {
@@ -207,11 +204,16 @@ export interface Navigation {
207
204
  export interface Page<Params extends Record<string, string> = Record<string, string>> {
208
205
  url: URL;
209
206
  params: Params;
207
+ routeId: string | null;
210
208
  stuff: App.Stuff;
211
209
  status: number;
212
210
  error: Error | null;
213
211
  }
214
212
 
213
+ export interface ParamValidator {
214
+ (param: string): boolean;
215
+ }
216
+
215
217
  /**
216
218
  * A function exported from an endpoint that corresponds to an
217
219
  * HTTP verb (`get`, `put`, `patch`, etc) and handles requests with
@@ -228,16 +230,11 @@ export interface RequestHandler<
228
230
  (event: RequestEvent<Params>): RequestHandlerOutput<Output>;
229
231
  }
230
232
 
231
- export type RequestHandlerOutput<Output extends ResponseBody = ResponseBody> = MaybePromise<
232
- Either<
233
- {
234
- status?: number;
235
- headers?: Headers | Partial<ResponseHeaders>;
236
- body?: Output;
237
- },
238
- Fallthrough
239
- >
240
- >;
233
+ export type RequestHandlerOutput<Output extends ResponseBody = ResponseBody> = MaybePromise<{
234
+ status?: number;
235
+ headers?: Headers | Partial<ResponseHeaders>;
236
+ body?: Output;
237
+ }>;
241
238
 
242
239
  export type ResponseBody = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
243
240
 
@@ -260,5 +257,6 @@ export interface SSRManifest {
260
257
  };
261
258
  nodes: SSRNodeLoader[];
262
259
  routes: SSRRoute[];
260
+ validators: () => Promise<Record<string, ParamValidator>>;
263
261
  };
264
262
  }