@sveltejs/kit 1.7.2 → 1.8.1

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.
@@ -1,5 +1,6 @@
1
1
  import { disable_search, make_trackable } from '../../../utils/url.js';
2
2
  import { unwrap_promises } from '../../../utils/promises.js';
3
+ import { DEV } from 'esm-env';
3
4
 
4
5
  /**
5
6
  * Calls the user's server `load` function.
@@ -14,6 +15,8 @@ import { unwrap_promises } from '../../../utils/promises.js';
14
15
  export async function load_server_data({ event, state, node, parent }) {
15
16
  if (!node?.server) return null;
16
17
 
18
+ let done = false;
19
+
17
20
  const uses = {
18
21
  dependencies: new Set(),
19
22
  params: new Set(),
@@ -23,6 +26,12 @@ export async function load_server_data({ event, state, node, parent }) {
23
26
  };
24
27
 
25
28
  const url = make_trackable(event.url, () => {
29
+ if (DEV && done && !uses.url) {
30
+ console.warn(
31
+ `${node.server_id}: Accessing URL properties in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the URL changes`
32
+ );
33
+ }
34
+
26
35
  uses.url = true;
27
36
  });
28
37
 
@@ -34,6 +43,13 @@ export async function load_server_data({ event, state, node, parent }) {
34
43
  ...event,
35
44
  fetch: (info, init) => {
36
45
  const url = new URL(info instanceof Request ? info.url : info, event.url);
46
+
47
+ if (DEV && done && !uses.dependencies.has(url.href)) {
48
+ console.warn(
49
+ `${node.server_id}: Calling \`event.fetch(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the dependency is invalidated`
50
+ );
51
+ }
52
+
37
53
  uses.dependencies.add(url.href);
38
54
 
39
55
  return event.fetch(info, init);
@@ -42,25 +58,54 @@ export async function load_server_data({ event, state, node, parent }) {
42
58
  depends: (...deps) => {
43
59
  for (const dep of deps) {
44
60
  const { href } = new URL(dep, event.url);
61
+
62
+ if (DEV && done && !uses.dependencies.has(href)) {
63
+ console.warn(
64
+ `${node.server_id}: Calling \`depends(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the dependency is invalidated`
65
+ );
66
+ }
67
+
45
68
  uses.dependencies.add(href);
46
69
  }
47
70
  },
48
71
  params: new Proxy(event.params, {
49
72
  get: (target, key) => {
73
+ if (DEV && done && typeof key === 'string' && !uses.params.has(key)) {
74
+ console.warn(
75
+ `${node.server_id}: Accessing \`params.${String(
76
+ key
77
+ )}\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the param changes`
78
+ );
79
+ }
80
+
50
81
  uses.params.add(key);
51
82
  return target[/** @type {string} */ (key)];
52
83
  }
53
84
  }),
54
85
  parent: async () => {
86
+ if (DEV && done && !uses.parent) {
87
+ console.warn(
88
+ `${node.server_id}: Calling \`parent(...)\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when parent data changes`
89
+ );
90
+ }
91
+
55
92
  uses.parent = true;
56
93
  return parent();
57
94
  },
58
- route: {
59
- get id() {
95
+ route: new Proxy(event.route, {
96
+ get: (target, key) => {
97
+ if (DEV && done && typeof key === 'string' && !uses.route) {
98
+ console.warn(
99
+ `${node.server_id}: Accessing \`route.${String(
100
+ key
101
+ )}\` in a promise handler after \`load(...)\` has returned will not cause the function to re-run when the route changes`
102
+ );
103
+ }
104
+
60
105
  uses.route = true;
61
- return event.route.id;
106
+ return target[/** @type {'id'} */ (key)];
62
107
  }
63
- },
108
+ }),
64
109
  url
65
110
  });
66
111
 
@@ -69,6 +114,8 @@ export async function load_server_data({ event, state, node, parent }) {
69
114
  validate_load_response(data, /** @type {string} */ (event.route.id));
70
115
  }
71
116
 
117
+ done = true;
118
+
72
119
  return {
73
120
  type: 'data',
74
121
  data,
@@ -89,7 +136,7 @@ export async function load_server_data({ event, state, node, parent }) {
89
136
  * state: import('types').SSRState;
90
137
  * csr: boolean;
91
138
  * }} opts
92
- * @returns {Promise<Record<string, any> | null>}
139
+ * @returns {Promise<Record<string, any | Promise<any>> | null>}
93
140
  */
94
141
  export async function load_data({
95
142
  event,
@@ -119,7 +166,10 @@ export async function load_data({
119
166
  });
120
167
 
121
168
  const data = result ? await unwrap_promises(result) : null;
122
- validate_load_response(data, /** @type {string} */ (event.route.id));
169
+ if (__SVELTEKIT_DEV__) {
170
+ validate_load_response(data, /** @type {string} */ (event.route.id));
171
+ }
172
+
123
173
  return data;
124
174
  }
125
175
 
@@ -7,9 +7,10 @@ import { serialize_data } from './serialize_data.js';
7
7
  import { s } from '../../../utils/misc.js';
8
8
  import { Csp } from './csp.js';
9
9
  import { uneval_action_response } from './actions.js';
10
- import { clarify_devalue_error } from '../utils.js';
11
- import { version, public_env } from '../../shared.js';
10
+ import { clarify_devalue_error, stringify_uses, handle_error_and_jsonify } from '../utils.js';
11
+ import { public_env } from '../../shared-server.js';
12
12
  import { text } from '../../../exports/index.js';
13
+ import { create_async_iterator } from '../../../utils/streaming.js';
13
14
 
14
15
  // TODO rename this function/module
15
16
 
@@ -18,6 +19,8 @@ const updated = {
18
19
  check: () => false
19
20
  };
20
21
 
22
+ const encoder = new TextEncoder();
23
+
21
24
  /**
22
25
  * Creates the HTML response.
23
26
  * @param {{
@@ -57,11 +60,11 @@ export async function render_response({
57
60
  }
58
61
  }
59
62
 
60
- const { entry } = manifest._;
63
+ const { client } = manifest._;
61
64
 
62
- const stylesheets = new Set(entry.stylesheets);
63
- const modulepreloads = new Set(entry.imports);
64
- const fonts = new Set(manifest._.entry.fonts);
65
+ const modulepreloads = new Set([...client.start.imports, ...client.app.imports]);
66
+ const stylesheets = new Set(client.app.stylesheets);
67
+ const fonts = new Set(client.app.fonts);
65
68
 
66
69
  /** @type {Set<string>} */
67
70
  const link_header_preloads = new Set();
@@ -141,17 +144,9 @@ export async function render_response({
141
144
  }
142
145
 
143
146
  for (const { node } of branch) {
144
- if (node.imports) {
145
- node.imports.forEach((url) => modulepreloads.add(url));
146
- }
147
-
148
- if (node.stylesheets) {
149
- node.stylesheets.forEach((url) => stylesheets.add(url));
150
- }
151
-
152
- if (node.fonts) {
153
- node.fonts.forEach((url) => fonts.add(url));
154
- }
147
+ for (const url of node.imports) modulepreloads.add(url);
148
+ for (const url of node.stylesheets) stylesheets.add(url);
149
+ for (const url of node.fonts) fonts.add(url);
155
150
 
156
151
  if (node.inline_styles) {
157
152
  Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
@@ -161,34 +156,42 @@ export async function render_response({
161
156
  rendered = { head: '', html: '', css: { code: '', map: null } };
162
157
  }
163
158
 
164
- let head = '';
165
- let body = rendered.html;
166
-
167
- const csp = new Csp(options.csp, {
168
- prerender: !!state.prerendering
169
- });
170
-
171
- const target = hash(body);
172
-
173
159
  /**
174
160
  * The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
175
161
  * @type {string}
176
162
  */
177
163
  let resolved_assets;
178
164
 
165
+ /**
166
+ * An expression that will evaluate in the client to determine the resolved asset path
167
+ */
168
+ let asset_expression;
169
+
179
170
  if (assets) {
180
171
  // if an asset path is specified, use it
181
172
  resolved_assets = assets;
173
+ asset_expression = s(assets);
182
174
  } else if (state.prerendering?.fallback) {
183
175
  // if we're creating a fallback page, asset paths need to be root-relative
184
176
  resolved_assets = base;
177
+ asset_expression = s(base);
185
178
  } else {
186
179
  // otherwise we want asset paths to be relative to the page, so that they
187
180
  // will work in odd contexts like IPFS, the internet archive, and so on
188
181
  const segments = event.url.pathname.slice(base.length).split('/').slice(2);
189
182
  resolved_assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
183
+ asset_expression = `new URL(${s(
184
+ resolved_assets
185
+ )}, location.href).pathname.replace(/^\\\/$/, '')`;
190
186
  }
191
187
 
188
+ let head = '';
189
+ let body = rendered.html;
190
+
191
+ const csp = new Csp(options.csp, {
192
+ prerender: !!state.prerendering
193
+ });
194
+
192
195
  /** @param {string} path */
193
196
  const prefixed = (path) => {
194
197
  if (path.startsWith('/')) {
@@ -200,48 +203,6 @@ export async function render_response({
200
203
  return `${resolved_assets}/${path}`;
201
204
  };
202
205
 
203
- const serialized = { data: '', form: 'null', error: 'null' };
204
-
205
- try {
206
- serialized.data = `[${branch
207
- .map(({ server_data }) => {
208
- if (server_data?.type === 'data') {
209
- const data = devalue.uneval(server_data.data);
210
-
211
- const uses = [];
212
- if (server_data.uses.dependencies.size > 0) {
213
- uses.push(`dependencies:${s(Array.from(server_data.uses.dependencies))}`);
214
- }
215
-
216
- if (server_data.uses.params.size > 0) {
217
- uses.push(`params:${s(Array.from(server_data.uses.params))}`);
218
- }
219
-
220
- if (server_data.uses.parent) uses.push(`parent:1`);
221
- if (server_data.uses.route) uses.push(`route:1`);
222
- if (server_data.uses.url) uses.push(`url:1`);
223
-
224
- return `{type:"data",data:${data},uses:{${uses.join(',')}}${
225
- server_data.slash ? `,slash:${s(server_data.slash)}` : ''
226
- }}`;
227
- }
228
-
229
- return s(server_data);
230
- })
231
- .join(',')}]`;
232
- } catch (e) {
233
- const error = /** @type {any} */ (e);
234
- throw new Error(clarify_devalue_error(event, error));
235
- }
236
-
237
- if (form_value) {
238
- serialized.form = uneval_action_response(form_value, /** @type {string} */ (event.route.id));
239
- }
240
-
241
- if (error) {
242
- serialized.error = devalue.uneval(error);
243
- }
244
-
245
206
  if (inline_styles.size > 0) {
246
207
  const content = Array.from(inline_styles.values()).join('\n');
247
208
 
@@ -289,18 +250,86 @@ export async function render_response({
289
250
  }
290
251
  }
291
252
 
253
+ const global = `__sveltekit_${options.version_hash}`;
254
+
255
+ const { data, chunks } = get_data(
256
+ event,
257
+ options,
258
+ branch.map((b) => b.server_data),
259
+ global
260
+ );
261
+
262
+ if (page_config.ssr && page_config.csr) {
263
+ body += `\n\t\t\t${fetched
264
+ .map((item) =>
265
+ serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
266
+ )
267
+ .join('\n\t\t\t')}`;
268
+ }
269
+
292
270
  if (page_config.csr) {
293
- const opts = [
294
- `assets: ${s(assets)}`,
271
+ const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
272
+ (path) => resolve_opts.preload({ type: 'js', path })
273
+ );
274
+
275
+ for (const path of included_modulepreloads) {
276
+ // we use modulepreload with the Link header for Chrome, along with
277
+ // <link rel="preload"> for Safari. This results in the fastest loading in
278
+ // the most used browsers, with no double-loading. Note that we need to use
279
+ // .mjs extensions for `preload` to behave like `modulepreload` in Chrome
280
+ link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
281
+ head += `\n\t\t<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
282
+ }
283
+
284
+ const blocks = [];
285
+
286
+ const properties = [
295
287
  `env: ${s(public_env)}`,
296
- `target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode`,
297
- `version: ${s(version)}`
288
+ `assets: ${asset_expression}`,
289
+ `element: document.currentScript.parentElement`
298
290
  ];
299
291
 
292
+ if (chunks) {
293
+ blocks.push(`const deferred = new Map();`);
294
+
295
+ properties.push(`defer: (id) => new Promise((fulfil, reject) => {
296
+ deferred.set(id, { fulfil, reject });
297
+ })`);
298
+
299
+ properties.push(`resolve: ({ id, data, error }) => {
300
+ const { fulfil, reject } = deferred.get(id);
301
+ deferred.delete(id);
302
+
303
+ if (error) reject(error);
304
+ else fulfil(data);
305
+ }`);
306
+ }
307
+
308
+ blocks.push(`${global} = {
309
+ ${properties.join(',\n\t\t\t\t\t\t')}
310
+ };`);
311
+
312
+ const args = [`app`, `${global}.element`];
313
+
300
314
  if (page_config.ssr) {
315
+ const serialized = { form: 'null', error: 'null' };
316
+
317
+ blocks.push(`const data = ${data};`);
318
+
319
+ if (form_value) {
320
+ serialized.form = uneval_action_response(
321
+ form_value,
322
+ /** @type {string} */ (event.route.id)
323
+ );
324
+ }
325
+
326
+ if (error) {
327
+ serialized.error = devalue.uneval(error);
328
+ }
329
+
301
330
  const hydrate = [
302
331
  `node_ids: [${branch.map(({ node }) => node.index).join(', ')}]`,
303
- `data: ${serialized.data}`,
332
+ `data`,
304
333
  `form: ${serialized.form}`,
305
334
  `error: ${serialized.error}`
306
335
  ];
@@ -313,67 +342,44 @@ export async function render_response({
313
342
  hydrate.push(`params: ${devalue.uneval(event.params)}`, `route: ${s(event.route)}`);
314
343
  }
315
344
 
316
- opts.push(`hydrate: {\n\t\t\t\t\t${hydrate.join(',\n\t\t\t\t\t')}\n\t\t\t\t}`);
345
+ args.push(`{\n\t\t\t\t\t\t\t${hydrate.join(',\n\t\t\t\t\t\t\t')}\n\t\t\t\t\t\t}`);
317
346
  }
318
347
 
319
- // prettier-ignore
320
- const init_app = `
321
- import { start } from ${s(prefixed(entry.file))};
322
-
323
- start({
324
- ${opts.join(',\n\t\t\t\t')}
325
- });
326
- `;
327
-
328
- for (const dep of modulepreloads) {
329
- const path = prefixed(dep);
330
-
331
- if (resolve_opts.preload({ type: 'js', path })) {
332
- link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
333
- if (state.prerendering) {
334
- head += `\n\t\t<link rel="modulepreload" href="${path}">`;
335
- }
336
- }
348
+ blocks.push(`Promise.all([
349
+ import(${s(prefixed(client.start.file))}),
350
+ import(${s(prefixed(client.app.file))})
351
+ ]).then(([kit, app]) => {
352
+ kit.start(${args.join(', ')});
353
+ });`);
354
+
355
+ if (options.service_worker) {
356
+ const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
357
+
358
+ // we use an anonymous function instead of an arrow function to support
359
+ // older browsers (https://github.com/sveltejs/kit/pull/5417)
360
+ blocks.push(`if ('serviceWorker' in navigator) {
361
+ addEventListener('load', function () {
362
+ navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
363
+ });
364
+ }`);
337
365
  }
338
366
 
339
- const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
340
-
367
+ const init_app = `
368
+ {
369
+ ${blocks.join('\n\n\t\t\t\t\t')}
370
+ }
371
+ `;
341
372
  csp.add_script(init_app);
342
373
 
343
- if (csp.script_needs_nonce) {
344
- attributes.push(`nonce="${csp.nonce}"`);
345
- }
346
-
347
- body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
374
+ body += `\n\t\t\t<script${
375
+ csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''
376
+ }>${init_app}</script>\n\t\t`;
348
377
  }
349
378
 
350
- if (page_config.ssr && page_config.csr) {
351
- body += `\n\t${fetched
352
- .map((item) =>
353
- serialize_data(item, resolve_opts.filterSerializedResponseHeaders, !!state.prerendering)
354
- )
355
- .join('\n\t')}`;
356
- }
357
-
358
- if (options.service_worker) {
359
- const opts = __SVELTEKIT_DEV__ ? `, { type: 'module' }` : '';
360
-
361
- // we use an anonymous function instead of an arrow function to support
362
- // older browsers (https://github.com/sveltejs/kit/pull/5417)
363
- const init_service_worker = `
364
- if ('serviceWorker' in navigator) {
365
- addEventListener('load', function () {
366
- navigator.serviceWorker.register('${prefixed('service-worker.js')}'${opts});
367
- });
368
- }
369
- `;
370
-
371
- // always include service worker unless it's turned off explicitly
372
- csp.add_script(init_service_worker);
373
-
374
- head += `
375
- <script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
376
- }
379
+ const headers = new Headers({
380
+ 'x-sveltekit-page': 'true',
381
+ 'content-type': 'text/html'
382
+ });
377
383
 
378
384
  if (state.prerendering) {
379
385
  // TODO read headers set with setHeaders and convert into http-equiv where possible
@@ -391,6 +397,19 @@ export async function render_response({
391
397
  if (http_equiv.length > 0) {
392
398
  head = http_equiv.join('\n') + head;
393
399
  }
400
+ } else {
401
+ const csp_header = csp.csp_provider.get_header();
402
+ if (csp_header) {
403
+ headers.set('content-security-policy', csp_header);
404
+ }
405
+ const report_only_header = csp.report_only_provider.get_header();
406
+ if (report_only_header) {
407
+ headers.set('content-security-policy-report-only', report_only_header);
408
+ }
409
+
410
+ if (link_header_preloads.size) {
411
+ headers.set('link', Array.from(link_header_preloads).join(', '));
412
+ }
394
413
  }
395
414
 
396
415
  // add the content after the script/css links so the link elements are parsed first
@@ -411,39 +430,124 @@ export async function render_response({
411
430
  done: true
412
431
  })) || '';
413
432
 
414
- if (DEV && page_config.csr) {
415
- if (transformed.split('<!--').length < html.split('<!--').length) {
416
- // the \u001B stuff is ANSI codes, so that we don't need to add a library to the runtime
417
- // https://svelte.dev/repl/1b3f49696f0c44c881c34587f2537aa2
418
- console.warn(
419
- "\u001B[1m\u001B[31mRemoving comments in transformPageChunk can break Svelte's hydration\u001B[39m\u001B[22m"
420
- );
433
+ if (!chunks) {
434
+ headers.set('etag', `"${hash(transformed)}"`);
435
+ }
436
+
437
+ if (DEV) {
438
+ if (page_config.csr) {
439
+ if (transformed.split('<!--').length < html.split('<!--').length) {
440
+ // the \u001B stuff is ANSI codes, so that we don't need to add a library to the runtime
441
+ // https://svelte.dev/repl/1b3f49696f0c44c881c34587f2537aa2
442
+ console.warn(
443
+ "\u001B[1m\u001B[31mRemoving comments in transformPageChunk can break Svelte's hydration\u001B[39m\u001B[22m"
444
+ );
445
+ }
446
+ } else {
447
+ if (chunks) {
448
+ console.warn(
449
+ '\u001B[1m\u001B[31mReturning promises from server `load` functions will only work if `csr === true`\u001B[39m\u001B[22m'
450
+ );
451
+ }
421
452
  }
422
453
  }
423
454
 
424
- const headers = new Headers({
425
- 'x-sveltekit-page': 'true',
426
- 'content-type': 'text/html',
427
- etag: `"${hash(transformed)}"`
428
- });
455
+ return !chunks
456
+ ? text(transformed, {
457
+ status,
458
+ headers
459
+ })
460
+ : new Response(
461
+ new ReadableStream({
462
+ async start(controller) {
463
+ controller.enqueue(encoder.encode(transformed));
464
+ for await (const chunk of chunks) {
465
+ controller.enqueue(encoder.encode(chunk));
466
+ }
467
+ controller.close();
468
+ },
469
+
470
+ type: 'bytes'
471
+ }),
472
+ {
473
+ headers: {
474
+ 'content-type': 'text/html'
475
+ }
476
+ }
477
+ );
478
+ }
429
479
 
430
- if (!state.prerendering) {
431
- const csp_header = csp.csp_provider.get_header();
432
- if (csp_header) {
433
- headers.set('content-security-policy', csp_header);
434
- }
435
- const report_only_header = csp.report_only_provider.get_header();
436
- if (report_only_header) {
437
- headers.set('content-security-policy-report-only', report_only_header);
438
- }
480
+ /**
481
+ * If the serialized data contains promises, `chunks` will be an
482
+ * async iterable containing their resolutions
483
+ * @param {import('types').RequestEvent} event
484
+ * @param {import('types').SSROptions} options
485
+ * @param {Array<import('types').ServerDataNode | null>} nodes
486
+ * @param {string} global
487
+ * @returns {{ data: string, chunks: AsyncIterable<string> | null }}
488
+ */
489
+ function get_data(event, options, nodes, global) {
490
+ let promise_id = 1;
491
+ let count = 0;
492
+
493
+ const { iterator, push, done } = create_async_iterator();
494
+
495
+ /** @param {any} thing */
496
+ function replacer(thing) {
497
+ if (typeof thing?.then === 'function') {
498
+ const id = promise_id++;
499
+ count += 1;
500
+
501
+ thing
502
+ .then(/** @param {any} data */ (data) => ({ data }))
503
+ .catch(
504
+ /** @param {any} error */ async (error) => ({
505
+ error: await handle_error_and_jsonify(event, options, error)
506
+ })
507
+ )
508
+ .then(
509
+ /**
510
+ * @param {{data: any; error: any}} result
511
+ */
512
+ async ({ data, error }) => {
513
+ count -= 1;
514
+
515
+ let str;
516
+ try {
517
+ str = devalue.uneval({ id, data, error }, replacer);
518
+ } catch (e) {
519
+ error = await handle_error_and_jsonify(
520
+ event,
521
+ options,
522
+ new Error(`Failed to serialize promise while rendering ${event.route.id}`)
523
+ );
524
+ data = undefined;
525
+ str = devalue.uneval({ id, data, error }, replacer);
526
+ }
527
+
528
+ push(`\n<script>${global}.resolve(${str})</script>`);
529
+ if (count === 0) done();
530
+ }
531
+ );
439
532
 
440
- if (link_header_preloads.size) {
441
- headers.set('link', Array.from(link_header_preloads).join(', '));
533
+ return `${global}.defer(${id})`;
442
534
  }
443
535
  }
444
536
 
445
- return text(transformed, {
446
- status,
447
- headers
448
- });
537
+ try {
538
+ const strings = nodes.map((node) => {
539
+ if (!node) return 'null';
540
+
541
+ return `{"type":"data","data":${devalue.uneval(node.data, replacer)},${stringify_uses(node)}${
542
+ node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''
543
+ }}`;
544
+ });
545
+
546
+ return {
547
+ data: `[${strings.join(',')}]`,
548
+ chunks: count > 0 ? iterator : null
549
+ };
550
+ } catch (e) {
551
+ throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
552
+ }
449
553
  }
@@ -1,5 +1,5 @@
1
1
  import { CookieSerializeOptions } from 'cookie';
2
- import { SSRNode, CspDirectives } from 'types';
2
+ import { SSRNode, CspDirectives, ServerDataNode } from 'types';
3
3
 
4
4
  export interface Fetched {
5
5
  url: string;
@@ -13,7 +13,7 @@ export interface Fetched {
13
13
  export type Loaded = {
14
14
  node: SSRNode;
15
15
  data: Record<string, any> | null;
16
- server_data: any;
16
+ server_data: ServerDataNode | null;
17
17
  };
18
18
 
19
19
  type CspMode = 'hash' | 'nonce' | 'auto';