@sveltejs/kit 2.30.1 → 2.31.0

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.
Files changed (38) hide show
  1. package/package.json +12 -1
  2. package/src/core/adapt/builder.js +122 -13
  3. package/src/core/config/options.js +6 -0
  4. package/src/exports/hooks/sequence.js +53 -31
  5. package/src/{runtime/app/server → exports/internal}/event.js +24 -9
  6. package/src/exports/internal/server.js +22 -0
  7. package/src/exports/public.d.ts +117 -5
  8. package/src/exports/vite/dev/index.js +8 -0
  9. package/src/exports/vite/index.js +30 -3
  10. package/src/exports/vite/utils.js +44 -0
  11. package/src/runtime/app/server/index.js +1 -1
  12. package/src/runtime/app/server/remote/command.js +4 -5
  13. package/src/runtime/app/server/remote/form.js +5 -7
  14. package/src/runtime/app/server/remote/prerender.js +5 -7
  15. package/src/runtime/app/server/remote/query.js +6 -8
  16. package/src/runtime/app/server/remote/shared.js +9 -13
  17. package/src/runtime/client/client.js +9 -14
  18. package/src/runtime/server/data/index.js +10 -5
  19. package/src/runtime/server/endpoint.js +4 -3
  20. package/src/runtime/server/page/actions.js +55 -24
  21. package/src/runtime/server/page/index.js +22 -5
  22. package/src/runtime/server/page/load_data.js +131 -121
  23. package/src/runtime/server/page/render.js +15 -7
  24. package/src/runtime/server/page/respond_with_error.js +7 -2
  25. package/src/runtime/server/remote.js +59 -13
  26. package/src/runtime/server/respond.js +110 -34
  27. package/src/runtime/server/utils.js +20 -5
  28. package/src/runtime/shared.js +20 -0
  29. package/src/runtime/telemetry/noop.js +81 -0
  30. package/src/runtime/telemetry/otel.js +21 -0
  31. package/src/runtime/telemetry/record_span.js +65 -0
  32. package/src/types/global-private.d.ts +2 -0
  33. package/src/types/internal.d.ts +28 -0
  34. package/src/types/synthetic/$env+dynamic+private.md +1 -1
  35. package/src/version.js +1 -1
  36. package/types/index.d.ts +117 -4
  37. package/types/index.d.ts.map +2 -2
  38. package/src/runtime/server/event-state.js +0 -40
@@ -15,8 +15,7 @@ import { SVELTE_KIT_ASSETS } from '../../../constants.js';
15
15
  import { SCHEME } from '../../../utils/url.js';
16
16
  import { create_server_routing_response, generate_route_object } from './server_routing.js';
17
17
  import { add_resolution_suffix } from '../../pathname.js';
18
- import { with_event } from '../../app/server/event.js';
19
- import { get_event_state } from '../event-state.js';
18
+ import { with_request_store } from '@sveltejs/kit/internal/server';
20
19
  import { text_encoder } from '../../utils.js';
21
20
 
22
21
  // TODO rename this function/module
@@ -38,6 +37,7 @@ const updated = {
38
37
  * status: number;
39
38
  * error: App.Error | null;
40
39
  * event: import('@sveltejs/kit').RequestEvent;
40
+ * event_state: import('types').RequestState;
41
41
  * resolve_opts: import('types').RequiredResolveOptions;
42
42
  * action_result?: import('@sveltejs/kit').ActionResult;
43
43
  * }} opts
@@ -52,6 +52,7 @@ export async function render_response({
52
52
  status,
53
53
  error = null,
54
54
  event,
55
+ event_state,
55
56
  resolve_opts,
56
57
  action_result
57
58
  }) {
@@ -190,14 +191,18 @@ export async function render_response({
190
191
  };
191
192
 
192
193
  try {
193
- rendered = with_event(event, () => options.root.render(props, render_opts));
194
+ rendered = with_request_store({ event, state: event_state }, () =>
195
+ options.root.render(props, render_opts)
196
+ );
194
197
  } finally {
195
198
  globalThis.fetch = fetch;
196
199
  paths.reset();
197
200
  }
198
201
  } else {
199
202
  try {
200
- rendered = with_event(event, () => options.root.render(props, render_opts));
203
+ rendered = with_request_store({ event, state: event_state }, () =>
204
+ options.root.render(props, render_opts)
205
+ );
201
206
  } finally {
202
207
  paths.reset();
203
208
  }
@@ -288,6 +293,7 @@ export async function render_response({
288
293
 
289
294
  const { data, chunks } = get_data(
290
295
  event,
296
+ event_state,
291
297
  options,
292
298
  branch.map((b) => b.server_data),
293
299
  csp,
@@ -377,7 +383,7 @@ export async function render_response({
377
383
  }`);
378
384
  }
379
385
 
380
- const { remote_data } = get_event_state(event);
386
+ const { remote_data } = event_state;
381
387
 
382
388
  if (remote_data) {
383
389
  /** @type {Record<string, any>} */
@@ -611,13 +617,14 @@ export async function render_response({
611
617
  * If the serialized data contains promises, `chunks` will be an
612
618
  * async iterable containing their resolutions
613
619
  * @param {import('@sveltejs/kit').RequestEvent} event
620
+ * @param {import('types').RequestState} event_state
614
621
  * @param {import('types').SSROptions} options
615
622
  * @param {Array<import('types').ServerDataNode | null>} nodes
616
623
  * @param {import('./csp.js').Csp} csp
617
624
  * @param {string} global
618
625
  * @returns {{ data: string, chunks: AsyncIterable<string> | null }}
619
626
  */
620
- function get_data(event, options, nodes, csp, global) {
627
+ function get_data(event, event_state, options, nodes, csp, global) {
621
628
  let promise_id = 1;
622
629
  let count = 0;
623
630
 
@@ -633,7 +640,7 @@ function get_data(event, options, nodes, csp, global) {
633
640
  .then(/** @param {any} data */ (data) => ({ data }))
634
641
  .catch(
635
642
  /** @param {any} error */ async (error) => ({
636
- error: await handle_error_and_jsonify(event, options, error)
643
+ error: await handle_error_and_jsonify(event, event_state, options, error)
637
644
  })
638
645
  )
639
646
  .then(
@@ -649,6 +656,7 @@ function get_data(event, options, nodes, csp, global) {
649
656
  } catch {
650
657
  error = await handle_error_and_jsonify(
651
658
  event,
659
+ event_state,
652
660
  options,
653
661
  new Error(`Failed to serialize promise while rendering ${event.route.id}`)
654
662
  );
@@ -12,6 +12,7 @@ import { PageNodes } from '../../../utils/page_nodes.js';
12
12
  /**
13
13
  * @param {{
14
14
  * event: import('@sveltejs/kit').RequestEvent;
15
+ * event_state: import('types').RequestState;
15
16
  * options: import('types').SSROptions;
16
17
  * manifest: import('@sveltejs/kit').SSRManifest;
17
18
  * state: import('types').SSRState;
@@ -22,6 +23,7 @@ import { PageNodes } from '../../../utils/page_nodes.js';
22
23
  */
23
24
  export async function respond_with_error({
24
25
  event,
26
+ event_state,
25
27
  options,
26
28
  manifest,
27
29
  state,
@@ -49,6 +51,7 @@ export async function respond_with_error({
49
51
 
50
52
  const server_data_promise = load_server_data({
51
53
  event,
54
+ event_state,
52
55
  state,
53
56
  node: default_layout,
54
57
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -59,6 +62,7 @@ export async function respond_with_error({
59
62
 
60
63
  const data = await load_data({
61
64
  event,
65
+ event_state,
62
66
  fetched,
63
67
  node: default_layout,
64
68
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -92,10 +96,11 @@ export async function respond_with_error({
92
96
  csr
93
97
  },
94
98
  status,
95
- error: await handle_error_and_jsonify(event, options, error),
99
+ error: await handle_error_and_jsonify(event, event_state, options, error),
96
100
  branch,
97
101
  fetched,
98
102
  event,
103
+ event_state,
99
104
  resolve_opts
100
105
  });
101
106
  } catch (e) {
@@ -108,7 +113,7 @@ export async function respond_with_error({
108
113
  return static_error_page(
109
114
  options,
110
115
  get_status(e),
111
- (await handle_error_and_jsonify(event, options, e)).message
116
+ (await handle_error_and_jsonify(event, event_state, options, e)).message
112
117
  );
113
118
  }
114
119
  }
@@ -1,25 +1,42 @@
1
1
  /** @import { ActionResult, RemoteForm, RequestEvent, SSRManifest } from '@sveltejs/kit' */
2
- /** @import { RemoteFunctionResponse, RemoteInfo, SSROptions } from 'types' */
2
+ /** @import { RemoteFunctionResponse, RemoteInfo, RequestState, SSROptions } from 'types' */
3
3
 
4
4
  import { json, error } from '@sveltejs/kit';
5
5
  import { HttpError, Redirect, SvelteKitError } from '@sveltejs/kit/internal';
6
+ import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
6
7
  import { app_dir, base } from '__sveltekit/paths';
7
- import { with_event } from '../app/server/event.js';
8
8
  import { is_form_content_type } from '../../utils/http.js';
9
9
  import { parse_remote_arg, stringify } from '../shared.js';
10
10
  import { handle_error_and_jsonify } from './utils.js';
11
11
  import { normalize_error } from '../../utils/error.js';
12
12
  import { check_incorrect_fail_use } from './page/actions.js';
13
13
  import { DEV } from 'esm-env';
14
- import { get_event_state } from './event-state.js';
14
+ import { record_span } from '../telemetry/record_span.js';
15
+
16
+ /** @type {typeof handle_remote_call_internal} */
17
+ export async function handle_remote_call(event, state, options, manifest, id) {
18
+ return record_span({
19
+ name: 'sveltekit.remote.call',
20
+ attributes: {
21
+ 'sveltekit.remote.call.id': id
22
+ },
23
+ fn: (current) => {
24
+ const traced_event = merge_tracing(event, current);
25
+ return with_request_store({ event: traced_event, state }, () =>
26
+ handle_remote_call_internal(traced_event, state, options, manifest, id)
27
+ );
28
+ }
29
+ });
30
+ }
15
31
 
16
32
  /**
17
33
  * @param {RequestEvent} event
34
+ * @param {RequestState} state
18
35
  * @param {SSROptions} options
19
36
  * @param {SSRManifest} manifest
20
37
  * @param {string} id
21
38
  */
22
- export async function handle_remote_call(event, options, manifest, id) {
39
+ async function handle_remote_call_internal(event, state, options, manifest, id) {
23
40
  const [hash, name, prerender_args] = id.split('/');
24
41
  const remotes = manifest._.remotes;
25
42
 
@@ -34,6 +51,11 @@ export async function handle_remote_call(event, options, manifest, id) {
34
51
  const info = fn.__;
35
52
  const transport = options.hooks.transport;
36
53
 
54
+ event.tracing.current.setAttributes({
55
+ 'sveltekit.remote.call.type': info.type,
56
+ 'sveltekit.remote.call.name': info.name
57
+ });
58
+
37
59
  /** @type {string[] | undefined} */
38
60
  let form_client_refreshes;
39
61
 
@@ -56,7 +78,7 @@ export async function handle_remote_call(event, options, manifest, id) {
56
78
  form_data.delete('sveltekit:remote_refreshes');
57
79
 
58
80
  const fn = info.fn;
59
- const data = await with_event(event, () => fn(form_data));
81
+ const data = await with_request_store({ event, state }, () => fn(form_data));
60
82
 
61
83
  return json(
62
84
  /** @type {RemoteFunctionResponse} */ ({
@@ -71,7 +93,7 @@ export async function handle_remote_call(event, options, manifest, id) {
71
93
  /** @type {{ payload: string, refreshes: string[] }} */
72
94
  const { payload, refreshes } = await event.request.json();
73
95
  const arg = parse_remote_arg(payload, transport);
74
- const data = await with_event(event, () => fn(arg));
96
+ const data = await with_request_store({ event, state }, () => fn(arg));
75
97
 
76
98
  return json(
77
99
  /** @type {RemoteFunctionResponse} */ ({
@@ -90,7 +112,9 @@ export async function handle_remote_call(event, options, manifest, id) {
90
112
  new URL(event.request.url).searchParams.get('payload')
91
113
  );
92
114
 
93
- const data = await with_event(event, () => fn(parse_remote_arg(payload, transport)));
115
+ const data = await with_request_store({ event, state }, () =>
116
+ fn(parse_remote_arg(payload, transport))
117
+ );
94
118
 
95
119
  return json(
96
120
  /** @type {RemoteFunctionResponse} */ ({
@@ -110,7 +134,7 @@ export async function handle_remote_call(event, options, manifest, id) {
110
134
  return json(
111
135
  /** @type {RemoteFunctionResponse} */ ({
112
136
  type: 'error',
113
- error: await handle_error_and_jsonify(event, options, error),
137
+ error: await handle_error_and_jsonify(event, state, options, error),
114
138
  status: error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500
115
139
  }),
116
140
  {
@@ -126,7 +150,7 @@ export async function handle_remote_call(event, options, manifest, id) {
126
150
  */
127
151
  async function serialize_refreshes(client_refreshes) {
128
152
  const refreshes = {
129
- ...get_event_state(event).refreshes,
153
+ ...state.refreshes,
130
154
  ...Object.fromEntries(
131
155
  await Promise.all(
132
156
  client_refreshes.map(async (key) => {
@@ -141,7 +165,12 @@ export async function handle_remote_call(event, options, manifest, id) {
141
165
 
142
166
  if (!fn) error(400, 'Bad Request');
143
167
 
144
- return [key, await with_event(event, () => fn(parse_remote_arg(payload, transport)))];
168
+ return [
169
+ key,
170
+ await with_request_store({ event, state }, () =>
171
+ fn(parse_remote_arg(payload, transport))
172
+ )
173
+ ];
145
174
  })
146
175
  )
147
176
  )
@@ -151,13 +180,30 @@ export async function handle_remote_call(event, options, manifest, id) {
151
180
  }
152
181
  }
153
182
 
183
+ /** @type {typeof handle_remote_form_post_internal} */
184
+ export async function handle_remote_form_post(event, state, manifest, id) {
185
+ return record_span({
186
+ name: 'sveltekit.remote.form.post',
187
+ attributes: {
188
+ 'sveltekit.remote.form.post.id': id
189
+ },
190
+ fn: (current) => {
191
+ const traced_event = merge_tracing(event, current);
192
+ return with_request_store({ event: traced_event, state }, () =>
193
+ handle_remote_form_post_internal(traced_event, state, manifest, id)
194
+ );
195
+ }
196
+ });
197
+ }
198
+
154
199
  /**
155
200
  * @param {RequestEvent} event
201
+ * @param {RequestState} state
156
202
  * @param {SSRManifest} manifest
157
203
  * @param {string} id
158
204
  * @returns {Promise<ActionResult>}
159
205
  */
160
- export async function handle_remote_form_post(event, manifest, id) {
206
+ async function handle_remote_form_post_internal(event, state, manifest, id) {
161
207
  const [hash, name, action_id] = id.split('/');
162
208
  const remotes = manifest._.remotes;
163
209
  const module = await remotes[hash]?.();
@@ -182,14 +228,14 @@ export async function handle_remote_form_post(event, manifest, id) {
182
228
 
183
229
  if (action_id) {
184
230
  // @ts-expect-error
185
- form = with_event(event, () => form.for(JSON.parse(action_id)));
231
+ form = with_request_store({ event, state }, () => form.for(JSON.parse(action_id)));
186
232
  }
187
233
 
188
234
  try {
189
235
  const form_data = await event.request.formData();
190
236
  const fn = /** @type {RemoteInfo & { type: 'form' }} */ (/** @type {any} */ (form).__).fn;
191
237
 
192
- await with_event(event, () => fn(form_data));
238
+ await with_request_store({ event, state }, () => fn(form_data));
193
239
 
194
240
  // We don't want the data to appear on `let { form } = $props()`, which is why we're not returning it.
195
241
  // It is instead available on `myForm.result`, setting of which happens within the remote `form` function.
@@ -1,6 +1,8 @@
1
+ /** @import { RequestState } from 'types' */
1
2
  import { DEV } from 'esm-env';
2
3
  import { json, text } from '@sveltejs/kit';
3
4
  import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
5
+ import { merge_tracing, with_request_store } from '@sveltejs/kit/internal/server';
4
6
  import { base, app_dir } from '__sveltekit/paths';
5
7
  import { is_endpoint_request, render_endpoint } from './endpoint.js';
6
8
  import { render_page } from './page/index.js';
@@ -34,8 +36,8 @@ import {
34
36
  strip_resolution_suffix
35
37
  } from '../pathname.js';
36
38
  import { get_remote_id, handle_remote_call } from './remote.js';
37
- import { with_event } from '../app/server/event.js';
38
- import { create_event_state, EVENT_STATE } from './event-state.js';
39
+ import { record_span } from '../telemetry/record_span.js';
40
+ import { otel } from '../telemetry/otel.js';
39
41
 
40
42
  /* global __SVELTEKIT_ADAPTER_NAME__ */
41
43
  /* global __SVELTEKIT_DEV__ */
@@ -55,6 +57,8 @@ const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']);
55
57
 
56
58
  let warned_on_devtools_json_request = false;
57
59
 
60
+ export const respond = propagate_context(internal_respond);
61
+
58
62
  /**
59
63
  * @param {Request} request
60
64
  * @param {import('types').SSROptions} options
@@ -62,7 +66,7 @@ let warned_on_devtools_json_request = false;
62
66
  * @param {import('types').SSRState} state
63
67
  * @returns {Promise<Response>}
64
68
  */
65
- export async function respond(request, options, manifest, state) {
69
+ export async function internal_respond(request, options, manifest, state) {
66
70
  /** URL but stripped from the potential `/__data.json` suffix and its search param */
67
71
  const url = new URL(request.url);
68
72
 
@@ -134,9 +138,18 @@ export async function respond(request, options, manifest, state) {
134
138
  url
135
139
  );
136
140
 
141
+ /** @type {RequestState} */
142
+ const event_state = {
143
+ prerendering: state.prerendering,
144
+ transport: options.hooks.transport,
145
+ handleValidationError: options.hooks.handleValidationError,
146
+ tracing: {
147
+ record_span
148
+ }
149
+ };
150
+
137
151
  /** @type {import('@sveltejs/kit').RequestEvent} */
138
152
  const event = {
139
- [EVENT_STATE]: create_event_state(state, options),
140
153
  cookies,
141
154
  // @ts-expect-error `fetch` needs to be created after the `event` itself
142
155
  fetch: null,
@@ -379,32 +392,69 @@ export async function respond(request, options, manifest, state) {
379
392
  disable_search(url);
380
393
  }
381
394
 
382
- const response = await with_event(event, () =>
383
- options.hooks.handle({
384
- event,
385
- resolve: (event, opts) =>
386
- // counter-intuitively, we need to clear the event, so that it's not
387
- // e.g. accessible when loading modules needed to handle the request
388
- with_event(null, () =>
389
- resolve(event, page_nodes, opts).then((response) => {
390
- // add headers/cookies here, rather than inside `resolve`, so that we
391
- // can do it once for all responses instead of once per `return`
392
- for (const key in headers) {
393
- const value = headers[key];
394
- response.headers.set(key, /** @type {string} */ (value));
395
- }
396
-
397
- add_cookies_to_headers(response.headers, Object.values(new_cookies));
398
-
399
- if (state.prerendering && event.route.id !== null) {
400
- response.headers.set('x-sveltekit-routeid', encodeURI(event.route.id));
401
- }
402
-
403
- return response;
404
- })
405
- )
406
- })
407
- );
395
+ const response = await record_span({
396
+ name: 'sveltekit.handle.root',
397
+ attributes: {
398
+ 'http.route': event.route.id || 'unknown',
399
+ 'http.method': event.request.method,
400
+ 'http.url': event.url.href,
401
+ 'sveltekit.is_data_request': is_data_request,
402
+ 'sveltekit.is_sub_request': event.isSubRequest
403
+ },
404
+ fn: async (root_span) => {
405
+ const traced_event = {
406
+ ...event,
407
+ tracing: {
408
+ enabled: __SVELTEKIT_SERVER_TRACING_ENABLED__,
409
+ root: root_span,
410
+ current: root_span
411
+ }
412
+ };
413
+ return await with_request_store({ event: traced_event, state: event_state }, () =>
414
+ options.hooks.handle({
415
+ event: traced_event,
416
+ resolve: (event, opts) => {
417
+ return record_span({
418
+ name: 'sveltekit.resolve',
419
+ attributes: {
420
+ 'http.route': event.route.id || 'unknown'
421
+ },
422
+ fn: (resolve_span) => {
423
+ // counter-intuitively, we need to clear the event, so that it's not
424
+ // e.g. accessible when loading modules needed to handle the request
425
+ return with_request_store(null, () =>
426
+ resolve(merge_tracing(event, resolve_span), page_nodes, opts).then(
427
+ (response) => {
428
+ // add headers/cookies here, rather than inside `resolve`, so that we
429
+ // can do it once for all responses instead of once per `return`
430
+ for (const key in headers) {
431
+ const value = headers[key];
432
+ response.headers.set(key, /** @type {string} */ (value));
433
+ }
434
+
435
+ add_cookies_to_headers(response.headers, Object.values(new_cookies));
436
+
437
+ if (state.prerendering && event.route.id !== null) {
438
+ response.headers.set('x-sveltekit-routeid', encodeURI(event.route.id));
439
+ }
440
+
441
+ resolve_span.setAttributes({
442
+ 'http.response.status_code': response.status,
443
+ 'http.response.body.size':
444
+ response.headers.get('content-length') || 'unknown'
445
+ });
446
+
447
+ return response;
448
+ }
449
+ )
450
+ );
451
+ }
452
+ });
453
+ }
454
+ })
455
+ );
456
+ }
457
+ });
408
458
 
409
459
  // respond with 304 if etag matches
410
460
  if (response.status === 200 && response.headers.has('etag')) {
@@ -460,7 +510,7 @@ export async function respond(request, options, manifest, state) {
460
510
  add_cookies_to_headers(response.headers, Object.values(new_cookies));
461
511
  return response;
462
512
  }
463
- return await handle_fatal_error(event, options, e);
513
+ return await handle_fatal_error(event, event_state, options, e);
464
514
  }
465
515
 
466
516
  /**
@@ -481,6 +531,7 @@ export async function respond(request, options, manifest, state) {
481
531
  if (options.hash_routing || state.prerendering?.fallback) {
482
532
  return await render_response({
483
533
  event,
534
+ event_state,
484
535
  options,
485
536
  manifest,
486
537
  state,
@@ -494,7 +545,7 @@ export async function respond(request, options, manifest, state) {
494
545
  }
495
546
 
496
547
  if (remote_id) {
497
- return await handle_remote_call(event, options, manifest, remote_id);
548
+ return await handle_remote_call(event, event_state, options, manifest, remote_id);
498
549
  }
499
550
 
500
551
  if (route) {
@@ -506,6 +557,7 @@ export async function respond(request, options, manifest, state) {
506
557
  if (is_data_request) {
507
558
  response = await render_data(
508
559
  event,
560
+ event_state,
509
561
  route,
510
562
  options,
511
563
  manifest,
@@ -514,13 +566,14 @@ export async function respond(request, options, manifest, state) {
514
566
  trailing_slash
515
567
  );
516
568
  } else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
517
- response = await render_endpoint(event, await route.endpoint(), state);
569
+ response = await render_endpoint(event, event_state, await route.endpoint(), state);
518
570
  } else if (route.page) {
519
571
  if (!page_nodes) {
520
572
  throw new Error('page_nodes not found. This should never happen');
521
573
  } else if (page_methods.has(method)) {
522
574
  response = await render_page(
523
575
  event,
576
+ event_state,
524
577
  route.page,
525
578
  options,
526
579
  manifest,
@@ -613,6 +666,7 @@ export async function respond(request, options, manifest, state) {
613
666
 
614
667
  return await respond_with_error({
615
668
  event,
669
+ event_state,
616
670
  options,
617
671
  manifest,
618
672
  state,
@@ -634,7 +688,7 @@ export async function respond(request, options, manifest, state) {
634
688
  // and I don't even know how to describe it. need to investigate at some point
635
689
 
636
690
  // HttpError from endpoint can end up here - TODO should it be handled there instead?
637
- return await handle_fatal_error(event, options, e);
691
+ return await handle_fatal_error(event, event_state, options, e);
638
692
  } finally {
639
693
  event.cookies.set = () => {
640
694
  throw new Error('Cannot use `cookies.set(...)` after the response has been generated');
@@ -658,3 +712,25 @@ export function load_page_nodes(page, manifest) {
658
712
  manifest._.nodes[page.leaf]()
659
713
  ]);
660
714
  }
715
+
716
+ /**
717
+ * It's likely that, in a distributed system, there are spans starting outside the SvelteKit server -- eg.
718
+ * started on the frontend client, or in a service that calls the SvelteKit server. There are standardized
719
+ * ways to represent this context in HTTP headers, so we can extract that context and run our tracing inside of it
720
+ * so that when our traces are exported, they are associated with the correct parent context.
721
+ * @param {typeof internal_respond} fn
722
+ * @returns {typeof internal_respond}
723
+ */
724
+ function propagate_context(fn) {
725
+ return async (req, ...rest) => {
726
+ if (otel === null) {
727
+ return fn(req, ...rest);
728
+ }
729
+
730
+ const { propagation, context } = await otel;
731
+ const c = propagation.extract(context.active(), Object.fromEntries(req.headers));
732
+ return context.with(c, async () => {
733
+ return await fn(req, ...rest);
734
+ });
735
+ };
736
+ }
@@ -1,12 +1,12 @@
1
1
  import { DEV } from 'esm-env';
2
2
  import { json, text } from '@sveltejs/kit';
3
3
  import { HttpError } from '@sveltejs/kit/internal';
4
+ import { with_request_store } from '@sveltejs/kit/internal/server';
4
5
  import { coalesce_to_error, get_message, get_status } from '../../utils/error.js';
5
6
  import { negotiate } from '../../utils/http.js';
6
7
  import { fix_stack_trace } from '../shared-server.js';
7
8
  import { ENDPOINT_METHODS } from '../../constants.js';
8
9
  import { escape_html } from '../../utils/escape.js';
9
- import { with_event } from '../app/server/event.js';
10
10
 
11
11
  /** @param {any} body */
12
12
  export function is_pojo(body) {
@@ -67,13 +67,14 @@ export function static_error_page(options, status, message) {
67
67
 
68
68
  /**
69
69
  * @param {import('@sveltejs/kit').RequestEvent} event
70
+ * @param {import('types').RequestState} state
70
71
  * @param {import('types').SSROptions} options
71
72
  * @param {unknown} error
72
73
  */
73
- export async function handle_fatal_error(event, options, error) {
74
+ export async function handle_fatal_error(event, state, options, error) {
74
75
  error = error instanceof HttpError ? error : coalesce_to_error(error);
75
76
  const status = get_status(error);
76
- const body = await handle_error_and_jsonify(event, options, error);
77
+ const body = await handle_error_and_jsonify(event, state, options, error);
77
78
 
78
79
  // ideally we'd use sec-fetch-dest instead, but Safari — quelle surprise — doesn't support it
79
80
  const type = negotiate(event.request.headers.get('accept') || 'text/html', [
@@ -92,11 +93,12 @@ export async function handle_fatal_error(event, options, error) {
92
93
 
93
94
  /**
94
95
  * @param {import('@sveltejs/kit').RequestEvent} event
96
+ * @param {import('types').RequestState} state
95
97
  * @param {import('types').SSROptions} options
96
98
  * @param {any} error
97
99
  * @returns {Promise<App.Error>}
98
100
  */
99
- export async function handle_error_and_jsonify(event, options, error) {
101
+ export async function handle_error_and_jsonify(event, state, options, error) {
100
102
  if (error instanceof HttpError) {
101
103
  return error.body;
102
104
  }
@@ -109,7 +111,7 @@ export async function handle_error_and_jsonify(event, options, error) {
109
111
  const message = get_message(error);
110
112
 
111
113
  return (
112
- (await with_event(event, () =>
114
+ (await with_request_store({ event, state }, () =>
113
115
  options.hooks.handleError({ error, event, status, message })
114
116
  )) ?? { message }
115
117
  );
@@ -183,3 +185,16 @@ export function has_prerendered_path(manifest, pathname) {
183
185
  (pathname.at(-1) === '/' && manifest._.prerendered_routes.has(pathname.slice(0, -1)))
184
186
  );
185
187
  }
188
+
189
+ /**
190
+ * Returns the filename without the extension. e.g., `+page.server`, `+page`, etc.
191
+ * @param {string | undefined} node_id
192
+ * @returns {string}
193
+ */
194
+ export function get_node_type(node_id) {
195
+ const parts = node_id?.split('/');
196
+ const filename = parts?.at(-1);
197
+ if (!filename) return 'unknown';
198
+ const dot_parts = filename.split('.');
199
+ return dot_parts.slice(0, -1).join('.');
200
+ }
@@ -19,6 +19,26 @@ export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
19
19
 
20
20
  export const TRAILING_SLASH_PARAM = 'x-sveltekit-trailing-slash';
21
21
 
22
+ /**
23
+ * @param {any} data
24
+ * @param {string} [location_description]
25
+ */
26
+ export function validate_load_response(data, location_description) {
27
+ if (data != null && Object.getPrototypeOf(data) !== Object.prototype) {
28
+ throw new Error(
29
+ `a load function ${location_description} returned ${
30
+ typeof data !== 'object'
31
+ ? `a ${typeof data}`
32
+ : data instanceof Response
33
+ ? 'a Response object'
34
+ : Array.isArray(data)
35
+ ? 'an array'
36
+ : 'a non-plain object'
37
+ }, but must return a plain object at the top level (i.e. \`return {...}\`)`
38
+ );
39
+ }
40
+ }
41
+
22
42
  /**
23
43
  * Try to `devalue.stringify` the data object using the provided transport encoders.
24
44
  * @param {any} data