@sveltejs/kit 1.0.0-next.431 → 1.0.0-next.434

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.
@@ -7,11 +7,12 @@ declare module '__GENERATED__/client-manifest.js' {
7
7
  export const nodes: CSRPageNodeLoader[];
8
8
 
9
9
  /**
10
- * A map of `[routeId: string]: [errors, layouts, page]` tuples, which
10
+ * A map of `[routeId: string]: [leaf, layouts, errors]` tuples, which
11
11
  * is parsed into an array of routes on startup. The numbers refer to the
12
- * indices in `nodes`.
12
+ * indices in `nodes`. The route layout and error nodes are not referenced,
13
+ * they are always number 0 and 1 and always apply.
13
14
  */
14
- export const dictionary: Record<string, [number[], number[], number]>;
15
+ export const dictionary: Record<string, [leaf: number, layouts?: number[], errors?: number[]]>;
15
16
 
16
17
  export const matchers: Record<string, ParamMatcher>;
17
18
  }
@@ -825,15 +825,10 @@ export function create_client({ target, base, trailing_slash }) {
825
825
  }
826
826
  }
827
827
 
828
- // TODO post-https://github.com/sveltejs/kit/discussions/6124, this will
829
- // no longer be necessary if we get here, it's because the root layout
830
- // load function failed, which means we have to fall back to the server
831
- return await load_root_error_page({
832
- status,
833
- error,
834
- url,
835
- routeId: route.id
836
- });
828
+ // if we get here, it's because the root `load` function failed,
829
+ // and we need to fall back to the server
830
+ native_navigation(url);
831
+ return;
837
832
  }
838
833
  } else {
839
834
  // push an empty slot so we can rewind past gaps to the
@@ -2,14 +2,19 @@ import { exec, parse_route_id } from '../../utils/routing.js';
2
2
 
3
3
  /**
4
4
  * @param {import('types').CSRPageNodeLoader[]} nodes
5
- * @param {Record<string, [number[], number[], number, 1?]>} dictionary
5
+ * @param {typeof import('__GENERATED__/client-manifest.js').dictionary} dictionary
6
6
  * @param {Record<string, (param: string) => boolean>} matchers
7
7
  * @returns {import('types').CSRRoute[]}
8
8
  */
9
9
  export function parse(nodes, dictionary, matchers) {
10
- return Object.entries(dictionary).map(([id, [errors, layouts, leaf, uses_server_data]]) => {
10
+ return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
11
11
  const { pattern, names, types } = parse_route_id(id);
12
12
 
13
+ // whether or not the route uses the server data is
14
+ // encoded using the ones' complement, to save space
15
+ const uses_server_data = leaf < 0;
16
+ if (uses_server_data) leaf = ~leaf;
17
+
13
18
  const route = {
14
19
  id,
15
20
  /** @param {string} path */
@@ -17,10 +22,10 @@ export function parse(nodes, dictionary, matchers) {
17
22
  const match = pattern.exec(path);
18
23
  if (match) return exec(match, names, types, matchers);
19
24
  },
20
- errors: errors.map((n) => nodes[n]),
21
- layouts: layouts.map((n) => nodes[n]),
25
+ errors: [1, ...(errors || [])].map((n) => nodes[n]),
26
+ layouts: [0, ...(layouts || [])].map((n) => nodes[n]),
22
27
  leaf: nodes[leaf],
23
- uses_server_data: !!uses_server_data
28
+ uses_server_data
24
29
  };
25
30
 
26
31
  // bit of a hack, but ensures that layout/error node lists are the same
@@ -3,14 +3,12 @@ import { check_method_names, method_not_allowed } from './utils.js';
3
3
 
4
4
  /**
5
5
  * @param {import('types').RequestEvent} event
6
- * @param {import('types').SSREndpoint} route
6
+ * @param {import('types').SSREndpoint} mod
7
7
  * @returns {Promise<Response>}
8
8
  */
9
- export async function render_endpoint(event, route) {
9
+ export async function render_endpoint(event, mod) {
10
10
  const method = /** @type {import('types').HttpMethod} */ (event.request.method);
11
11
 
12
- const mod = await route.load();
13
-
14
12
  // TODO: Remove for 1.0
15
13
  check_method_names(mod);
16
14
 
@@ -21,12 +19,6 @@ export async function render_endpoint(event, route) {
21
19
  }
22
20
 
23
21
  if (!handler) {
24
- if (event.request.headers.get('x-sveltekit-load')) {
25
- // TODO would be nice to avoid these requests altogether,
26
- // by noting whether or not page endpoints export `get`
27
- return new Response(undefined, { status: 204 });
28
- }
29
-
30
22
  return method_not_allowed(mod, method);
31
23
  }
32
24
 
@@ -93,7 +93,7 @@ export async function respond(request, options, state) {
93
93
  }
94
94
 
95
95
  if (route) {
96
- if (route.type === 'page') {
96
+ if (route.page) {
97
97
  const normalized = normalize_path(url.pathname, options.trailing_slash);
98
98
 
99
99
  if (normalized !== url.pathname && !state.prerendering?.fallback) {
@@ -253,9 +253,9 @@ export async function respond(request, options, state) {
253
253
  if (route) {
254
254
  /** @type {Response} */
255
255
  let response;
256
- if (is_data_request && route.type === 'page') {
256
+ if (is_data_request && route.page) {
257
257
  try {
258
- const node_ids = [...route.layouts, route.leaf];
258
+ const node_ids = [...route.page.layouts, route.page.leaf];
259
259
 
260
260
  const invalidated =
261
261
  request.headers.get('x-sveltekit-invalidated')?.split(',').map(Boolean) ??
@@ -317,7 +317,8 @@ export async function respond(request, options, state) {
317
317
  throw error;
318
318
  }
319
319
 
320
- length = i + 1; // don't include nodes after first error
320
+ // Math.min because array isn't guaranteed to resolve in order
321
+ length = Math.min(length, i + 1);
321
322
 
322
323
  if (error instanceof HttpError) {
323
324
  return /** @type {import('types').ServerErrorNode} */ ({
@@ -358,11 +359,14 @@ export async function respond(request, options, state) {
358
359
  response = json(error_to_pojo(error, options.get_stack), { status: 500 });
359
360
  }
360
361
  }
362
+ } else if (route.page) {
363
+ response = await render_page(event, route, route.page, options, state, resolve_opts);
364
+ } else if (route.endpoint) {
365
+ response = await render_endpoint(event, await route.endpoint());
361
366
  } else {
362
- response =
363
- route.type === 'endpoint'
364
- ? await render_endpoint(event, route)
365
- : await render_page(event, route, options, state, resolve_opts);
367
+ // a route will always have a page or an endpoint, but TypeScript
368
+ // doesn't know that
369
+ throw new Error('This should never happen');
366
370
  }
367
371
 
368
372
  if (!is_data_request) {
@@ -9,7 +9,7 @@ import { domain_matches, path_matches } from './cookie.js';
9
9
  * event: import('types').RequestEvent;
10
10
  * options: import('types').SSROptions;
11
11
  * state: import('types').SSRState;
12
- * route: import('types').SSRPage | import('types').SSRErrorPage;
12
+ * route: import('types').SSRRoute | import('types').SSRErrorPage;
13
13
  * }} opts
14
14
  */
15
15
  export function create_fetch({ event, options, state, route }) {
@@ -5,6 +5,7 @@ import { method_not_allowed, error_to_pojo, allowed_methods } from '../utils.js'
5
5
  import { create_fetch } from './fetch.js';
6
6
  import { HttpError, Redirect } from '../../../index/private.js';
7
7
  import { error, json } from '../../../index/index.js';
8
+ import { compact } from '../../../utils/array.js';
8
9
  import { normalize_error } from '../../../utils/error.js';
9
10
  import { load_data, load_server_data } from './load_data.js';
10
11
 
@@ -17,13 +18,14 @@ import { load_data, load_server_data } from './load_data.js';
17
18
 
18
19
  /**
19
20
  * @param {import('types').RequestEvent} event
20
- * @param {import('types').SSRPage} route
21
+ * @param {import('types').SSRRoute} route
22
+ * @param {import('types').PageNodeIndexes} page
21
23
  * @param {import('types').SSROptions} options
22
24
  * @param {import('types').SSRState} state
23
25
  * @param {import('types').RequiredResolveOptions} resolve_opts
24
26
  * @returns {Promise<Response>}
25
27
  */
26
- export async function render_page(event, route, options, state, resolve_opts) {
28
+ export async function render_page(event, route, page, options, state, resolve_opts) {
27
29
  if (state.initiator === route) {
28
30
  // infinite request cycle detected
29
31
  return new Response(`Not found: ${event.url.pathname}`, {
@@ -41,7 +43,7 @@ export async function render_page(event, route, options, state, resolve_opts) {
41
43
  event.request.method !== 'GET' &&
42
44
  event.request.method !== 'HEAD'
43
45
  ) {
44
- const node = await options.manifest._.nodes[route.leaf]();
46
+ const node = await options.manifest._.nodes[page.leaf]();
45
47
  if (node.server) {
46
48
  return handle_json_request(event, options, node.server);
47
49
  }
@@ -52,8 +54,8 @@ export async function render_page(event, route, options, state, resolve_opts) {
52
54
  try {
53
55
  const nodes = await Promise.all([
54
56
  // we use == here rather than === because [undefined] serializes as "[null]"
55
- ...route.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())),
56
- options.manifest._.nodes[route.leaf]()
57
+ ...page.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())),
58
+ options.manifest._.nodes[page.leaf]()
57
59
  ]);
58
60
 
59
61
  const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1));
@@ -244,8 +246,8 @@ export async function render_page(event, route, options, state, resolve_opts) {
244
246
  const status = error instanceof HttpError ? error.status : 500;
245
247
 
246
248
  while (i--) {
247
- if (route.errors[i]) {
248
- const index = /** @type {number} */ (route.errors[i]);
249
+ if (page.errors[i]) {
250
+ const index = /** @type {number} */ (page.errors[i]);
249
251
  const node = await options.manifest._.nodes[index]();
250
252
 
251
253
  let j = i;
@@ -400,18 +402,3 @@ function redirect_response(status, location) {
400
402
  headers: { location }
401
403
  });
402
404
  }
403
-
404
- /**
405
- * @template T
406
- * @param {Array<T | null>} array
407
- * @returns {T[]}
408
- */
409
- function compact(array) {
410
- const compacted = [];
411
- for (const item of array) {
412
- if (item) {
413
- compacted.push(item);
414
- }
415
- }
416
- return compacted;
417
- }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Removes nullish values from an array.
3
+ *
4
+ * @template T
5
+ * @param {Array<T>} arr
6
+ */
7
+ export function compact(arr) {
8
+ return arr.filter(/** @returns {val is NonNullable<T>} */ (val) => val != null);
9
+ }
@@ -12,12 +12,21 @@ 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
+
15
23
  const pattern =
16
24
  id === ''
17
25
  ? /^\/$/
18
26
  : new RegExp(
19
27
  `^${id
20
- .split(/(?:@[a-zA-Z0-9_-]+)?(?:\/|$)/)
28
+ .split(/(?:\/|$)/)
29
+ .filter(affects_path)
21
30
  .map((segment, i, segments) => {
22
31
  const decoded_segment = decodeURIComponent(segment);
23
32
  // special case — /[...rest]/ could contain zero segments
@@ -79,6 +88,14 @@ export function parse_route_id(id) {
79
88
  return { pattern, names, types };
80
89
  }
81
90
 
91
+ /**
92
+ * Returns `false` for `(group)` segments
93
+ * @param {string} segment
94
+ */
95
+ export function affects_path(segment) {
96
+ return !/^\([^)]+\)$/.test(segment);
97
+ }
98
+
82
99
  /**
83
100
  * @param {RegExpMatchArray} match
84
101
  * @param {string[]} names
@@ -106,3 +123,15 @@ export function exec(match, names, types, matchers) {
106
123
 
107
124
  return params;
108
125
  }
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
+ }
@@ -84,7 +84,12 @@ export class Server {
84
84
  };
85
85
  }
86
86
 
87
- init({ env }) {
87
+ /**
88
+ * Take care: Some adapters may have to call \`Server.init\` per-request to set env vars,
89
+ * so anything that shouldn't be rerun should be wrapped in an \`if\` block to make sure it hasn't
90
+ * been done already.
91
+ */
92
+ async init({ env }) {
88
93
  const entries = Object.entries(env);
89
94
 
90
95
  const prv = Object.fromEntries(entries.filter(([k]) => !k.startsWith('${
@@ -99,12 +104,6 @@ export class Server {
99
104
  set_public_env(pub);
100
105
 
101
106
  this.options.public_env = pub;
102
- }
103
-
104
- async respond(request, options = {}) {
105
- if (!(request instanceof Request)) {
106
- throw new Error('The first argument to server.respond must be a Request object. See https://github.com/sveltejs/kit/pull/3384 for details');
107
- }
108
107
 
109
108
  if (!this.options.hooks) {
110
109
  const module = await import(${s(hooks)});
@@ -114,6 +113,12 @@ export class Server {
114
113
  externalFetch: module.externalFetch || fetch
115
114
  };
116
115
  }
116
+ }
117
+
118
+ async respond(request, options = {}) {
119
+ if (!(request instanceof Request)) {
120
+ throw new Error('The first argument to server.respond must be a Request object. See https://github.com/sveltejs/kit/pull/3384 for details');
121
+ }
117
122
 
118
123
  return respond(request, this.options, options);
119
124
  }
@@ -158,8 +163,8 @@ export async function build_server(options, client) {
158
163
 
159
164
  // add entry points for every endpoint...
160
165
  manifest_data.routes.forEach((route) => {
161
- if (route.type === 'endpoint') {
162
- const resolved = path.resolve(cwd, route.file);
166
+ if (route.endpoint) {
167
+ const resolved = path.resolve(cwd, route.endpoint.file);
163
168
  const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved));
164
169
  const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, '')));
165
170
  input[name] = resolved;
@@ -326,10 +331,16 @@ function get_methods(cwd, output, manifest_data) {
326
331
  /** @type {Record<string, import('types').HttpMethod[]>} */
327
332
  const methods = {};
328
333
  manifest_data.routes.forEach((route) => {
329
- const file = route.type === 'endpoint' ? route.file : route.leaf.server;
334
+ if (route.endpoint) {
335
+ if (lookup[route.endpoint.file]) {
336
+ methods[route.endpoint.file] = lookup[route.endpoint.file].filter(is_http_method);
337
+ }
338
+ }
330
339
 
331
- if (file && lookup[file]) {
332
- methods[file] = lookup[file].filter(is_http_method);
340
+ if (route.leaf?.server) {
341
+ if (lookup[route.leaf.server]) {
342
+ methods[route.leaf.server] = lookup[route.leaf.server].filter(is_http_method);
343
+ }
333
344
  }
334
345
  });
335
346
 
@@ -7,12 +7,12 @@ import { getRequest, setResponse } from '../../node/index.js';
7
7
  import { installPolyfills } from '../../node/polyfills.js';
8
8
  import { coalesce_to_error } from '../../utils/error.js';
9
9
  import { posixify } from '../../utils/filesystem.js';
10
- import { parse_route_id } from '../../utils/routing.js';
11
10
  import { load_template } from '../../core/config/index.js';
12
11
  import { SVELTE_KIT_ASSETS } from '../../core/constants.js';
13
12
  import * as sync from '../../core/sync/sync.js';
14
13
  import { get_mime_lookup, runtime_base, runtime_prefix } from '../../core/utils.js';
15
14
  import { get_env, prevent_illegal_vite_imports, resolve_entry } from '../utils.js';
15
+ import { compact } from '../../utils/array.js';
16
16
 
17
17
  // Vite doesn't expose this so we just copy the list for now
18
18
  // https://github.com/vitejs/vite/blob/3edd1af56e980aef56641a5a51cf2932bb580d41/packages/vite/src/node/plugins/css.ts#L96
@@ -149,36 +149,27 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
149
149
  return result;
150
150
  };
151
151
  }),
152
- routes: manifest_data.routes.map((route) => {
153
- const { pattern, names, types } = parse_route_id(route.id);
152
+ routes: compact(
153
+ manifest_data.routes.map((route) => {
154
+ if (!route.page && !route.endpoint) return null;
155
+
156
+ const endpoint = route.endpoint;
154
157
 
155
- if (route.type === 'page') {
156
158
  return {
157
- type: 'page',
158
159
  id: route.id,
159
- pattern,
160
- names,
161
- types,
162
- errors: route.errors.map((id) => (id ? manifest_data.nodes.indexOf(id) : undefined)),
163
- layouts: route.layouts.map((id) =>
164
- id ? manifest_data.nodes.indexOf(id) : undefined
165
- ),
166
- leaf: manifest_data.nodes.indexOf(route.leaf)
160
+ pattern: route.pattern,
161
+ names: route.names,
162
+ types: route.types,
163
+ page: route.page,
164
+ endpoint: endpoint
165
+ ? async () => {
166
+ const url = path.resolve(cwd, endpoint.file);
167
+ return await vite.ssrLoadModule(url);
168
+ }
169
+ : null
167
170
  };
168
- }
169
-
170
- return {
171
- type: 'endpoint',
172
- id: route.id,
173
- pattern,
174
- names,
175
- types,
176
- load: async () => {
177
- const url = path.resolve(cwd, route.file);
178
- return await vite.ssrLoadModule(url);
179
- }
180
- };
181
- }),
171
+ })
172
+ ),
182
173
  matchers: async () => {
183
174
  /** @type {Record<string, import('types').ParamMatcher>} */
184
175
  const matchers = {};
@@ -229,15 +220,16 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
229
220
  to_run();
230
221
  }, 100);
231
222
  };
223
+
232
224
  // Debounce add/unlink events because in case of folder deletion or moves
233
225
  // they fire in rapid succession, causing needless invocations.
234
226
  watch('add', () => debounce(update_manifest));
235
227
  watch('unlink', () => debounce(update_manifest));
236
228
  watch('change', (file) => {
237
229
  // Don't run for a single file if the whole manifest is about to get updated
238
- if (!timeout) {
239
- sync.update(svelte_config, manifest_data, file);
240
- }
230
+ if (timeout) return;
231
+
232
+ sync.update(svelte_config, manifest_data, file);
241
233
  });
242
234
 
243
235
  const assets = svelte_config.kit.paths.assets ? SVELTE_KIT_ASSETS : svelte_config.kit.paths.base;
package/src/vite/index.js CHANGED
@@ -411,7 +411,13 @@ function kit() {
411
411
 
412
412
  const child = fork(
413
413
  script,
414
- [vite_config.build.outDir, results_path, manifest_path, '' + verbose],
414
+ [
415
+ vite_config.build.outDir,
416
+ results_path,
417
+ manifest_path,
418
+ '' + verbose,
419
+ JSON.stringify({ ...env.private, ...env.public })
420
+ ],
415
421
  {
416
422
  stdio: 'inherit'
417
423
  }
@@ -45,7 +45,7 @@ export async function preview(vite, vite_config, svelte_config) {
45
45
  });
46
46
 
47
47
  const server = new Server(manifest);
48
- server.init({
48
+ await server.init({
49
49
  env: loadEnv(vite_config.mode, process.cwd(), '')
50
50
  });
51
51
 
package/types/index.d.ts CHANGED
@@ -264,7 +264,7 @@ export interface ResolveOptions {
264
264
 
265
265
  export class Server {
266
266
  constructor(manifest: SSRManifest);
267
- init(options: ServerInitOptions): void;
267
+ init(options: ServerInitOptions): Promise<void>;
268
268
  respond(request: Request, options: RequestOptions): Promise<Response>;
269
269
  }
270
270
 
@@ -80,13 +80,6 @@ export type CSRRoute = {
80
80
  uses_server_data: boolean;
81
81
  };
82
82
 
83
- export interface EndpointData {
84
- type: 'endpoint';
85
- id: string;
86
- pattern: RegExp;
87
- file: string;
88
- }
89
-
90
83
  export type GetParams = (match: RegExpExecArray) => Record<string, string>;
91
84
 
92
85
  export interface Hooks {
@@ -101,7 +94,7 @@ export interface ImportNode {
101
94
  }
102
95
 
103
96
  export class InternalServer extends Server {
104
- init(options: ServerInitOptions): void;
97
+ init(options: ServerInitOptions): Promise<void>;
105
98
  respond(
106
99
  request: Request,
107
100
  options: RequestOptions & {
@@ -123,18 +116,12 @@ export interface MethodOverride {
123
116
  }
124
117
 
125
118
  export interface PageNode {
119
+ depth: number;
126
120
  component?: string; // TODO supply default component if it's missing (bit of an edge case)
127
121
  shared?: string;
128
122
  server?: string;
129
- }
130
-
131
- export interface PageData {
132
- type: 'page';
133
- id: string;
134
- pattern: RegExp;
135
- errors: Array<PageNode | undefined>;
136
- layouts: Array<PageNode | undefined>;
137
- leaf: PageNode;
123
+ parent_id?: string;
124
+ parent?: PageNode;
138
125
  }
139
126
 
140
127
  export type PayloadScriptAttributes =
@@ -167,7 +154,34 @@ export interface Respond {
167
154
  (request: Request, options: SSROptions, state: SSRState): Promise<Response>;
168
155
  }
169
156
 
170
- export type RouteData = PageData | EndpointData;
157
+ /**
158
+ * Represents a route segment in the app. It can either be an intermediate node
159
+ * with only layout/error pages, or a leaf, at which point either `page` and `leaf`
160
+ * or `endpoint` is set.
161
+ */
162
+ export interface RouteData {
163
+ id: string;
164
+ parent: RouteData | null;
165
+
166
+ segment: string;
167
+ pattern: RegExp;
168
+ names: string[];
169
+ types: string[];
170
+
171
+ layout: PageNode | null;
172
+ error: PageNode | null;
173
+ leaf: PageNode | null;
174
+
175
+ page: {
176
+ layouts: Array<number | undefined>;
177
+ errors: Array<number | undefined>;
178
+ leaf: number;
179
+ } | null;
180
+
181
+ endpoint: {
182
+ file: string;
183
+ } | null;
184
+ }
171
185
 
172
186
  export type ServerData =
173
187
  | {
@@ -228,15 +242,6 @@ export interface SSRComponent {
228
242
 
229
243
  export type SSRComponentLoader = () => Promise<SSRComponent>;
230
244
 
231
- export interface SSREndpoint {
232
- type: 'endpoint';
233
- id: string;
234
- pattern: RegExp;
235
- names: string[];
236
- types: string[];
237
- load(): Promise<Partial<Record<HttpMethod, RequestHandler>>>;
238
- }
239
-
240
245
  export interface SSRNode {
241
246
  component: SSRComponentLoader;
242
247
  /** index into the `components` array in client-manifest.js */
@@ -309,27 +314,33 @@ export interface SSROptions {
309
314
  trailing_slash: TrailingSlash;
310
315
  }
311
316
 
312
- export interface SSRPage {
313
- type: 'page';
314
- id: string;
315
- pattern: RegExp;
316
- names: string[];
317
- types: string[];
317
+ export interface SSRErrorPage {
318
+ id: '__error';
319
+ }
320
+
321
+ export interface PageNodeIndexes {
318
322
  errors: Array<number | undefined>;
319
323
  layouts: Array<number | undefined>;
320
324
  leaf: number;
321
325
  }
322
326
 
323
- export interface SSRErrorPage {
324
- id: '__error';
325
- }
327
+ export type SSREndpoint = Partial<Record<HttpMethod, RequestHandler>>;
326
328
 
327
- export type SSRRoute = SSREndpoint | SSRPage;
329
+ export interface SSRRoute {
330
+ id: string;
331
+ pattern: RegExp;
332
+ names: string[];
333
+ types: string[];
334
+
335
+ page: PageNodeIndexes | null;
336
+
337
+ endpoint: (() => Promise<SSREndpoint>) | null;
338
+ }
328
339
 
329
340
  export interface SSRState {
330
341
  fallback?: string;
331
342
  getClientAddress: () => string;
332
- initiator?: SSRPage | SSRErrorPage;
343
+ initiator?: SSRRoute | SSRErrorPage;
333
344
  platform?: any;
334
345
  prerendering?: PrerenderOptions;
335
346
  }