@sveltejs/kit 2.30.0 → 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 (44) 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/core/sync/sync.js +3 -2
  5. package/src/core/sync/write_non_ambient.js +78 -2
  6. package/src/core/sync/write_types/index.js +0 -72
  7. package/src/exports/hooks/sequence.js +53 -31
  8. package/src/{runtime/app/server → exports/internal}/event.js +24 -9
  9. package/src/exports/internal/server.js +22 -0
  10. package/src/exports/public.d.ts +117 -6
  11. package/src/exports/vite/dev/index.js +8 -0
  12. package/src/exports/vite/index.js +30 -3
  13. package/src/exports/vite/utils.js +44 -0
  14. package/src/runtime/app/paths/index.js +3 -1
  15. package/src/runtime/app/paths/types.d.ts +0 -1
  16. package/src/runtime/app/server/index.js +1 -1
  17. package/src/runtime/app/server/remote/command.js +4 -5
  18. package/src/runtime/app/server/remote/form.js +5 -7
  19. package/src/runtime/app/server/remote/prerender.js +5 -7
  20. package/src/runtime/app/server/remote/query.js +6 -8
  21. package/src/runtime/app/server/remote/shared.js +9 -13
  22. package/src/runtime/client/client.js +9 -14
  23. package/src/runtime/server/data/index.js +10 -5
  24. package/src/runtime/server/endpoint.js +4 -3
  25. package/src/runtime/server/page/actions.js +55 -24
  26. package/src/runtime/server/page/index.js +22 -5
  27. package/src/runtime/server/page/load_data.js +131 -121
  28. package/src/runtime/server/page/render.js +15 -7
  29. package/src/runtime/server/page/respond_with_error.js +7 -2
  30. package/src/runtime/server/remote.js +59 -13
  31. package/src/runtime/server/respond.js +110 -34
  32. package/src/runtime/server/utils.js +20 -5
  33. package/src/runtime/shared.js +20 -0
  34. package/src/runtime/telemetry/noop.js +81 -0
  35. package/src/runtime/telemetry/otel.js +21 -0
  36. package/src/runtime/telemetry/record_span.js +65 -0
  37. package/src/types/ambient.d.ts +54 -0
  38. package/src/types/global-private.d.ts +2 -0
  39. package/src/types/internal.d.ts +28 -0
  40. package/src/types/synthetic/$env+dynamic+private.md +1 -1
  41. package/src/version.js +1 -1
  42. package/types/index.d.ts +171 -4
  43. package/types/index.d.ts.map +2 -2
  44. package/src/runtime/server/event-state.js +0 -40
@@ -17,14 +17,14 @@ import {
17
17
  RouteSegment
18
18
  } from '../types/private.js';
19
19
  import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
20
- import type { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
21
- import type { StandardSchemaV1 } from '@standard-schema/spec';
20
+ import { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
21
+ import { StandardSchemaV1 } from '@standard-schema/spec';
22
22
  import {
23
23
  RouteId as AppRouteId,
24
24
  LayoutParams as AppLayoutParams,
25
25
  ResolvedPathname
26
- // @ts-ignore
27
26
  } from '$app/types';
27
+ import { Span } from '@opentelemetry/api';
28
28
 
29
29
  export { PrerenderOption } from '../types/private.js';
30
30
 
@@ -50,6 +50,12 @@ export interface Adapter {
50
50
  * @param details.config The merged route config
51
51
  */
52
52
  read?: (details: { config: any; route: { id: string } }) => boolean;
53
+
54
+ /**
55
+ * Test support for `instrumentation.server.js`. To pass, the adapter must support running `instrumentation.server.js` prior to the application code.
56
+ * @since 2.31.0
57
+ */
58
+ instrumentation?: () => boolean;
53
59
  };
54
60
  /**
55
61
  * Creates an `Emulator`, which allows the adapter to influence the environment
@@ -187,6 +193,47 @@ export interface Builder {
187
193
  }
188
194
  ) => string[];
189
195
 
196
+ /**
197
+ * Check if the server instrumentation file exists.
198
+ * @returns true if the server instrumentation file exists, false otherwise
199
+ * @since 2.31.0
200
+ */
201
+ hasServerInstrumentationFile: () => boolean;
202
+
203
+ /**
204
+ * Instrument `entrypoint` with `instrumentation`.
205
+ *
206
+ * Renames `entrypoint` to `start` and creates a new module at
207
+ * `entrypoint` which imports `instrumentation` and then dynamically imports `start`. This allows
208
+ * the module hooks necessary for instrumentation libraries to be loaded prior to any application code.
209
+ *
210
+ * Caveats:
211
+ * - "Live exports" will not work. If your adapter uses live exports, your users will need to manually import the server instrumentation on startup.
212
+ * - If `tla` is `false`, OTEL auto-instrumentation may not work properly. Use it if your environment supports it.
213
+ * - Use `hasServerInstrumentationFile` to check if the user has a server instrumentation file; if they don't, you shouldn't do this.
214
+ *
215
+ * @param options an object containing the following properties:
216
+ * @param options.entrypoint the path to the entrypoint to trace.
217
+ * @param options.instrumentation the path to the instrumentation file.
218
+ * @param options.start the name of the start file. This is what `entrypoint` will be renamed to.
219
+ * @param options.module configuration for the resulting entrypoint module.
220
+ * @param options.module.exports
221
+ * @param options.module.generateText a function that receives the relative paths to the instrumentation and start files, and generates the text of the module to be traced. If not provided, the default implementation will be used, which uses top-level await.
222
+ * @since 2.31.0
223
+ */
224
+ instrument: (args: {
225
+ entrypoint: string;
226
+ instrumentation: string;
227
+ start?: string;
228
+ module?:
229
+ | {
230
+ exports: string[];
231
+ }
232
+ | {
233
+ generateText: (args: { instrumentation: string; start: string }) => string;
234
+ };
235
+ }) => void;
236
+
190
237
  /**
191
238
  * Compress files in `directory` with gzip and brotli, where appropriate. Generates `.gz` and `.br` files alongside the originals.
192
239
  * @param {string} directory The directory containing the files to be compressed
@@ -408,10 +455,34 @@ export interface KitConfig {
408
455
  */
409
456
  privatePrefix?: string;
410
457
  };
411
- /**
412
- * Experimental features which are exempt from semantic versioning. These features may be changed or removed at any time.
413
- */
458
+ /** Experimental features. Here be dragons. These are not subject to semantic versioning, so breaking changes or removal can happen in any release. */
414
459
  experimental?: {
460
+ /**
461
+ * Options for enabling server-side [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
462
+ * @default { server: false, serverFile: false }
463
+ * @since 2.31.0
464
+ */
465
+ tracing?: {
466
+ /**
467
+ * Enables server-side [OpenTelemetry](https://opentelemetry.io/) span emission for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
468
+ * @default false
469
+ * @since 2.31.0
470
+ */
471
+ server?: boolean;
472
+ };
473
+
474
+ /**
475
+ * @since 2.31.0
476
+ */
477
+ instrumentation?: {
478
+ /**
479
+ * Enables `instrumentation.server.js` for tracing and observability instrumentation.
480
+ * @default false
481
+ * @since 2.31.0
482
+ */
483
+ server?: boolean;
484
+ };
485
+
415
486
  /**
416
487
  * Whether to enable the experimental remote functions feature. This feature is not yet stable and may be changed or removed at any time.
417
488
  * @default false
@@ -1027,6 +1098,19 @@ export interface LoadEvent<
1027
1098
  * ```
1028
1099
  */
1029
1100
  untrack: <T>(fn: () => T) => T;
1101
+
1102
+ /**
1103
+ * Access to spans for tracing. If tracing is not enabled or the function is being run in the browser, these spans will do nothing.
1104
+ * @since 2.31.0
1105
+ */
1106
+ tracing: {
1107
+ /** Whether tracing is enabled. */
1108
+ enabled: boolean;
1109
+ /** The root span for the request. This span is named `sveltekit.handle.root`. */
1110
+ root: Span;
1111
+ /** The span associated with the current `load` function. */
1112
+ current: Span;
1113
+ };
1030
1114
  }
1031
1115
 
1032
1116
  export interface NavigationEvent<
@@ -1305,6 +1389,20 @@ export interface RequestEvent<
1305
1389
  * `true` for `+server.js` calls coming from SvelteKit without the overhead of actually making an HTTP request. This happens when you make same-origin `fetch` requests on the server.
1306
1390
  */
1307
1391
  isSubRequest: boolean;
1392
+
1393
+ /**
1394
+ * Access to spans for tracing. If tracing is not enabled, these spans will do nothing.
1395
+ * @since 2.31.0
1396
+ */
1397
+ tracing: {
1398
+ /** Whether tracing is enabled. */
1399
+ enabled: boolean;
1400
+ /** The root span for the request. This span is named `sveltekit.handle.root`. */
1401
+ root: Span;
1402
+ /** The span associated with the current `handle` hook, `load` function, or form action. */
1403
+ current: Span;
1404
+ };
1405
+
1308
1406
  /**
1309
1407
  * `true` if the request comes from the client via a remote function. The `url` property will be stripped of the internal information
1310
1408
  * related to the data request in this case. Use this property instead if the distinction is important to you.
@@ -1468,6 +1566,19 @@ export interface ServerLoadEvent<
1468
1566
  * ```
1469
1567
  */
1470
1568
  untrack: <T>(fn: () => T) => T;
1569
+
1570
+ /**
1571
+ * Access to spans for tracing. If tracing is not enabled, these spans will do nothing.
1572
+ * @since 2.31.0
1573
+ */
1574
+ tracing: {
1575
+ /** Whether tracing is enabled. */
1576
+ enabled: boolean;
1577
+ /** The root span for the request. This span is named `sveltekit.handle.root`. */
1578
+ root: Span;
1579
+ /** The span associated with the current server `load` function. */
1580
+ current: Span;
1581
+ };
1471
1582
  }
1472
1583
 
1473
1584
  /**
@@ -501,6 +501,14 @@ export async function dev(vite, vite_config, svelte_config) {
501
501
  return;
502
502
  }
503
503
 
504
+ const resolved_instrumentation = resolve_entry(
505
+ path.join(svelte_config.kit.files.src, 'instrumentation.server')
506
+ );
507
+
508
+ if (resolved_instrumentation) {
509
+ await vite.ssrLoadModule(resolved_instrumentation);
510
+ }
511
+
504
512
  // we have to import `Server` before calling `set_assets`
505
513
  const { Server } = /** @type {import('types').ServerModule} */ (
506
514
  await vite.ssrLoadModule(`${runtime_base}/server/index.js`, { fixStacktrace: true })
@@ -16,7 +16,13 @@ import { build_service_worker } from './build/build_service_worker.js';
16
16
  import { assets_base, find_deps, resolve_symlinks } from './build/utils.js';
17
17
  import { dev } from './dev/index.js';
18
18
  import { preview } from './preview/index.js';
19
- import { get_config_aliases, get_env, normalize_id, stackless } from './utils.js';
19
+ import {
20
+ error_for_missing_config,
21
+ get_config_aliases,
22
+ get_env,
23
+ normalize_id,
24
+ stackless
25
+ } from './utils.js';
20
26
  import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
21
27
  import prerender from '../../core/postbuild/prerender.js';
22
28
  import analyse from '../../core/postbuild/analyse.js';
@@ -332,7 +338,8 @@ async function kit({ svelte_config }) {
332
338
  __SVELTEKIT_DEV__: 'false',
333
339
  __SVELTEKIT_EMBEDDED__: s(kit.embedded),
334
340
  __SVELTEKIT_EXPERIMENTAL__REMOTE_FUNCTIONS__: s(kit.experimental.remoteFunctions),
335
- __SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client' ? 'true' : 'false',
341
+ __SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
342
+ __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.instrumentation.server),
336
343
  __SVELTEKIT_PAYLOAD__: new_config.build.ssr
337
344
  ? '{}'
338
345
  : `globalThis.__sveltekit_${version_hash}`
@@ -347,7 +354,8 @@ async function kit({ svelte_config }) {
347
354
  __SVELTEKIT_DEV__: 'true',
348
355
  __SVELTEKIT_EMBEDDED__: s(kit.embedded),
349
356
  __SVELTEKIT_EXPERIMENTAL__REMOTE_FUNCTIONS__: s(kit.experimental.remoteFunctions),
350
- __SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client' ? 'true' : 'false',
357
+ __SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
358
+ __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.instrumentation.server),
351
359
  __SVELTEKIT_PAYLOAD__: 'globalThis.__sveltekit_dev'
352
360
  };
353
361
 
@@ -775,6 +783,25 @@ async function kit({ svelte_config }) {
775
783
  input[name] = path.resolve(file);
776
784
  });
777
785
 
786
+ // ...and the server instrumentation file
787
+ const server_instrumentation = resolve_entry(
788
+ path.join(kit.files.src, 'instrumentation.server')
789
+ );
790
+ if (server_instrumentation) {
791
+ const { adapter } = kit;
792
+ if (adapter && !adapter.supports?.instrumentation?.()) {
793
+ throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
794
+ }
795
+ if (!kit.experimental.instrumentation.server) {
796
+ error_for_missing_config(
797
+ 'instrumentation.server.js',
798
+ 'kit.experimental.instrumentation.server',
799
+ 'true'
800
+ );
801
+ }
802
+ input['instrumentation.server'] = server_instrumentation;
803
+ }
804
+
778
805
  // ...and every .remote file
779
806
  for (const remote of manifest_data.remotes) {
780
807
  input[`remote/${remote.hash}`] = path.resolve(remote.file);
@@ -4,6 +4,7 @@ import { posixify } from '../../utils/filesystem.js';
4
4
  import { negotiate } from '../../utils/http.js';
5
5
  import { filter_private_env, filter_public_env } from '../../utils/env.js';
6
6
  import { escape_html } from '../../utils/escape.js';
7
+ import { dedent } from '../../core/sync/utils.js';
7
8
  import {
8
9
  app_server,
9
10
  env_dynamic_private,
@@ -172,3 +173,46 @@ export function stackless(message) {
172
173
  }
173
174
 
174
175
  export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
176
+
177
+ /**
178
+ * For `error_for_missing_config('instrumentation.server.js', 'kit.experimental.instrumentation.server', true)`,
179
+ * returns:
180
+ *
181
+ * ```
182
+ * To enable `instrumentation.server.js`, add the following to your `svelte.config.js`:
183
+ *
184
+ *\`\`\`js
185
+ * kit:
186
+ * experimental:
187
+ * instrumentation:
188
+ * server: true
189
+ * }
190
+ * }
191
+ * }
192
+ *\`\`\`
193
+ *```
194
+ * @param {string} feature_name
195
+ * @param {string} path
196
+ * @param {string} value
197
+ * @returns {never}
198
+ */
199
+ export function error_for_missing_config(feature_name, path, value) {
200
+ const hole = '__HOLE__';
201
+
202
+ const result = path.split('.').reduce((acc, part, i, parts) => {
203
+ const indent = ' '.repeat(i);
204
+ const rhs = i === parts.length - 1 ? value : `{\n${hole}\n${indent}}`;
205
+
206
+ return acc.replace(hole, `${indent}${part}: ${rhs}`);
207
+ }, hole);
208
+
209
+ throw new Error(
210
+ dedent`\
211
+ To enable \`${feature_name}\`, add the following to your \`svelte.config.js\`:
212
+
213
+ \`\`\`js
214
+ ${result}
215
+ \`\`\`
216
+ `
217
+ );
218
+ }
@@ -8,7 +8,9 @@ export function asset(file) {
8
8
 
9
9
  /** @type {import('./types.d.ts').resolve} */
10
10
  export function resolve(id, params) {
11
- return base + resolve_route(id, params);
11
+ // The type error is correct here, and if someone doesn't pass params when they should there's a runtime error,
12
+ // but we don't want to adjust the internal resolve_route function to accept `undefined`, hence the type cast.
13
+ return base + resolve_route(id, /** @type {Record<string, string>} */ (params));
12
14
  }
13
15
 
14
16
  export { base, assets, resolve as resolveRoute };
@@ -1,4 +1,3 @@
1
- // @ts-ignore
2
1
  import { Asset, RouteId, RouteParams, Pathname, ResolvedPathname } from '$app/types';
3
2
 
4
3
  /**
@@ -73,6 +73,6 @@ export function read(asset) {
73
73
  throw new Error(`Asset does not exist: ${file}`);
74
74
  }
75
75
 
76
- export { getRequestEvent } from './event.js';
76
+ export { getRequestEvent } from '@sveltejs/kit/internal/server';
77
77
 
78
78
  export { query, prerender, command, form } from './remote/index.js';
@@ -1,9 +1,8 @@
1
1
  /** @import { RemoteCommand } from '@sveltejs/kit' */
2
2
  /** @import { RemoteInfo, MaybePromise } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
- import { getRequestEvent } from '../event.js';
4
+ import { get_request_store } from '@sveltejs/kit/internal/server';
5
5
  import { check_experimental, create_validator, run_remote_function } from './shared.js';
6
- import { get_event_state } from '../../../server/event-state.js';
7
6
 
8
7
  /**
9
8
  * Creates a remote command. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -65,7 +64,7 @@ export function command(validate_or_fn, maybe_fn) {
65
64
 
66
65
  /** @type {RemoteCommand<Input, Output> & { __: RemoteInfo }} */
67
66
  const wrapper = (arg) => {
68
- const event = getRequestEvent();
67
+ const { event, state } = get_request_store();
69
68
 
70
69
  if (!event.isRemoteRequest) {
71
70
  throw new Error(
@@ -73,9 +72,9 @@ export function command(validate_or_fn, maybe_fn) {
73
72
  );
74
73
  }
75
74
 
76
- get_event_state(event).refreshes ??= {};
75
+ state.refreshes ??= {};
77
76
 
78
- const promise = Promise.resolve(run_remote_function(event, true, arg, validate, fn));
77
+ const promise = Promise.resolve(run_remote_function(event, state, true, arg, validate, fn));
79
78
 
80
79
  // @ts-expect-error
81
80
  promise.updates = () => {
@@ -1,8 +1,7 @@
1
1
  /** @import { RemoteForm } from '@sveltejs/kit' */
2
2
  /** @import { RemoteInfo, MaybePromise } from 'types' */
3
- import { getRequestEvent } from '../event.js';
3
+ import { get_request_store } from '@sveltejs/kit/internal/server';
4
4
  import { check_experimental, run_remote_function } from './shared.js';
5
- import { get_event_state } from '../../../server/event-state.js';
6
5
 
7
6
  /**
8
7
  * Creates a form object that can be spread onto a `<form>` element.
@@ -57,12 +56,11 @@ export function form(fn) {
57
56
  id: '',
58
57
  /** @param {FormData} form_data */
59
58
  fn: async (form_data) => {
60
- const event = getRequestEvent();
61
- const state = get_event_state(event);
59
+ const { event, state } = get_request_store();
62
60
 
63
61
  state.refreshes ??= {};
64
62
 
65
- const result = await run_remote_function(event, true, form_data, (d) => d, fn);
63
+ const result = await run_remote_function(event, state, true, form_data, (d) => d, fn);
66
64
 
67
65
  // We don't need to care about args or deduplicating calls, because uneval results are only relevant in full page reloads
68
66
  // where only one form submission is active at the same time
@@ -89,7 +87,7 @@ export function form(fn) {
89
87
  Object.defineProperty(instance, 'result', {
90
88
  get() {
91
89
  try {
92
- const { remote_data } = get_event_state(getRequestEvent());
90
+ const { remote_data } = get_request_store().state;
93
91
  return remote_data?.[__.id];
94
92
  } catch {
95
93
  return undefined;
@@ -111,7 +109,7 @@ export function form(fn) {
111
109
  Object.defineProperty(instance, 'for', {
112
110
  /** @type {RemoteForm<any>['for']} */
113
111
  value: (key) => {
114
- const state = get_event_state(getRequestEvent());
112
+ const { state } = get_request_store();
115
113
  let instance = (state.form_instances ??= new Map()).get(key);
116
114
 
117
115
  if (!instance) {
@@ -3,7 +3,7 @@
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
4
  import { error, json } from '@sveltejs/kit';
5
5
  import { DEV } from 'esm-env';
6
- import { getRequestEvent } from '../event.js';
6
+ import { get_request_store } from '@sveltejs/kit/internal/server';
7
7
  import { create_remote_cache_key, stringify, stringify_remote_arg } from '../../../shared.js';
8
8
  import { app_dir, base } from '__sveltekit/paths';
9
9
  import {
@@ -13,7 +13,6 @@ import {
13
13
  parse_remote_response,
14
14
  run_remote_function
15
15
  } from './shared.js';
16
- import { get_event_state } from '../../../server/event-state.js';
17
16
 
18
17
  /**
19
18
  * Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -93,15 +92,14 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
93
92
  const wrapper = (arg) => {
94
93
  /** @type {Promise<Output> & Partial<RemoteResource<Output>>} */
95
94
  const promise = (async () => {
96
- const event = getRequestEvent();
97
- const state = get_event_state(event);
95
+ const { event, state } = get_request_store();
98
96
  const payload = stringify_remote_arg(arg, state.transport);
99
97
  const id = __.id;
100
98
  const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
101
99
 
102
100
  if (!state.prerendering && !DEV && !event.isRemoteRequest) {
103
101
  try {
104
- return await get_response(id, arg, event, async () => {
102
+ return await get_response(id, arg, state, async () => {
105
103
  // TODO adapters can provide prerendered data more efficiently than
106
104
  // fetching from the public internet
107
105
  const response = await fetch(new URL(url, event.url.origin).href);
@@ -130,8 +128,8 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
130
128
  return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
131
129
  }
132
130
 
133
- const promise = get_response(id, arg, event, () =>
134
- run_remote_function(event, false, arg, validate, fn)
131
+ const promise = get_response(id, arg, state, () =>
132
+ run_remote_function(event, state, false, arg, validate, fn)
135
133
  );
136
134
 
137
135
  if (state.prerendering) {
@@ -1,7 +1,7 @@
1
1
  /** @import { RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
2
2
  /** @import { RemoteInfo, MaybePromise } from 'types' */
3
3
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
4
- import { getRequestEvent } from '../event.js';
4
+ import { get_request_store } from '@sveltejs/kit/internal/server';
5
5
  import { create_remote_cache_key, stringify_remote_arg } from '../../../shared.js';
6
6
  import { prerendering } from '__sveltekit/environment';
7
7
  import {
@@ -10,7 +10,6 @@ import {
10
10
  get_response,
11
11
  run_remote_function
12
12
  } from './shared.js';
13
- import { get_event_state } from '../../../server/event-state.js';
14
13
 
15
14
  /**
16
15
  * Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -78,19 +77,18 @@ export function query(validate_or_fn, maybe_fn) {
78
77
  );
79
78
  }
80
79
 
81
- const event = getRequestEvent();
80
+ const { event, state } = get_request_store();
82
81
 
83
82
  /** @type {Promise<any> & Partial<RemoteQuery<any>>} */
84
- const promise = get_response(__.id, arg, event, () =>
85
- run_remote_function(event, false, arg, validate, fn)
83
+ const promise = get_response(__.id, arg, state, () =>
84
+ run_remote_function(event, state, false, arg, validate, fn)
86
85
  );
87
86
 
88
87
  promise.catch(() => {});
89
88
 
90
89
  promise.refresh = async () => {
91
- const event = getRequestEvent();
92
- const state = get_event_state(event);
93
- const refreshes = state?.refreshes;
90
+ const { state } = get_request_store();
91
+ const refreshes = state.refreshes;
94
92
 
95
93
  if (!refreshes) {
96
94
  throw new Error(
@@ -1,10 +1,9 @@
1
1
  /** @import { RequestEvent } from '@sveltejs/kit' */
2
- /** @import { ServerHooks, MaybePromise } from 'types' */
2
+ /** @import { ServerHooks, MaybePromise, RequestState } from 'types' */
3
3
  import { parse } from 'devalue';
4
4
  import { error } from '@sveltejs/kit';
5
- import { getRequestEvent, with_event } from '../event.js';
5
+ import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
6
6
  import { create_remote_cache_key, stringify_remote_arg } from '../../../shared.js';
7
- import { EVENT_STATE, get_event_state } from '../../../server/event-state.js';
8
7
 
9
8
  /**
10
9
  * @param {any} validate_or_fn
@@ -30,8 +29,7 @@ export function create_validator(validate_or_fn, maybe_fn) {
30
29
  if ('~standard' in validate_or_fn) {
31
30
  return async (arg) => {
32
31
  // Get event before async validation to ensure it's available in server environments without AsyncLocalStorage, too
33
- const event = getRequestEvent();
34
- const state = get_event_state(event);
32
+ const { event, state } = get_request_store();
35
33
  const validate = validate_or_fn['~standard'].validate;
36
34
 
37
35
  const result = await validate(arg);
@@ -66,12 +64,11 @@ export function create_validator(validate_or_fn, maybe_fn) {
66
64
  * @template {MaybePromise<any>} T
67
65
  * @param {string} id
68
66
  * @param {any} arg
69
- * @param {RequestEvent} event
67
+ * @param {RequestState} state
70
68
  * @param {() => Promise<T>} get_result
71
69
  * @returns {Promise<T>}
72
70
  */
73
- export function get_response(id, arg, event, get_result) {
74
- const state = get_event_state(event);
71
+ export function get_response(id, arg, state, get_result) {
75
72
  const cache_key = create_remote_cache_key(id, stringify_remote_arg(arg, state.transport));
76
73
 
77
74
  return ((state.remote_data ??= {})[cache_key] ??= get_result());
@@ -104,17 +101,16 @@ export function parse_remote_response(data, transport) {
104
101
  * Like `with_event` but removes things from `event` you cannot see/call in remote functions, such as `setHeaders`.
105
102
  * @template T
106
103
  * @param {RequestEvent} event
104
+ * @param {RequestState} state
107
105
  * @param {boolean} allow_cookies
108
106
  * @param {any} arg
109
107
  * @param {(arg: any) => any} validate
110
108
  * @param {(arg?: any) => T} fn
111
109
  */
112
- export async function run_remote_function(event, allow_cookies, arg, validate, fn) {
110
+ export async function run_remote_function(event, state, allow_cookies, arg, validate, fn) {
113
111
  /** @type {RequestEvent} */
114
112
  const cleansed = {
115
113
  ...event,
116
- // @ts-expect-error this isn't part of the public `RequestEvent` type
117
- [EVENT_STATE]: event[EVENT_STATE],
118
114
  setHeaders: () => {
119
115
  throw new Error('setHeaders is not allowed in remote functions');
120
116
  },
@@ -148,6 +144,6 @@ export async function run_remote_function(event, allow_cookies, arg, validate, f
148
144
  };
149
145
 
150
146
  // In two parts, each with_event, so that runtimes without async local storage can still get the event at the start of the function
151
- const validated = await with_event(cleansed, () => validate(arg));
152
- return with_event(cleansed, () => fn(validated));
147
+ const validated = await with_request_store({ event: cleansed, state }, () => validate(arg));
148
+ return with_request_store({ event: cleansed, state }, () => fn(validated));
153
149
  }
@@ -39,11 +39,17 @@ import {
39
39
  } from './constants.js';
40
40
  import { validate_page_exports } from '../../utils/exports.js';
41
41
  import { compact } from '../../utils/array.js';
42
- import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM, validate_depends } from '../shared.js';
42
+ import {
43
+ INVALIDATED_PARAM,
44
+ TRAILING_SLASH_PARAM,
45
+ validate_depends,
46
+ validate_load_response
47
+ } from '../shared.js';
43
48
  import { get_message, get_status } from '../../utils/error.js';
44
49
  import { writable } from 'svelte/store';
45
50
  import { page, update, navigating } from './state.svelte.js';
46
51
  import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
52
+ import { noop_span } from '../telemetry/noop.js';
47
53
  import { text_decoder } from '../utils.js';
48
54
 
49
55
  export { load_css };
@@ -715,6 +721,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
715
721
 
716
722
  /** @type {import('@sveltejs/kit').LoadEvent} */
717
723
  const load_input = {
724
+ tracing: { enabled: false, root: noop_span, current: noop_span },
718
725
  route: new Proxy(route, {
719
726
  get: (target, key) => {
720
727
  if (is_tracking) {
@@ -807,19 +814,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
807
814
  try {
808
815
  lock_fetch();
809
816
  data = (await node.universal.load.call(null, load_input)) ?? null;
810
- if (data != null && Object.getPrototypeOf(data) !== Object.prototype) {
811
- throw new Error(
812
- `a load function related to route '${route.id}' returned ${
813
- typeof data !== 'object'
814
- ? `a ${typeof data}`
815
- : data instanceof Response
816
- ? 'a Response object'
817
- : Array.isArray(data)
818
- ? 'an array'
819
- : 'a non-plain object'
820
- }, but must return a plain object at the top level (i.e. \`return {...}\`)`
821
- );
822
- }
817
+ validate_load_response(data, `related to route '${route.id}'`);
823
818
  } finally {
824
819
  unlock_fetch();
825
820
  }