@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.30.0",
3
+ "version": "2.31.0",
4
4
  "description": "SvelteKit is the fastest way to build Svelte apps",
5
5
  "keywords": [
6
6
  "framework",
@@ -33,6 +33,7 @@
33
33
  "sirv": "^3.0.0"
34
34
  },
35
35
  "devDependencies": {
36
+ "@opentelemetry/api": "^1.0.0",
36
37
  "@playwright/test": "^1.51.1",
37
38
  "@sveltejs/vite-plugin-svelte": "^6.0.0-next.3",
38
39
  "@types/connect": "^3.4.38",
@@ -48,9 +49,15 @@
48
49
  },
49
50
  "peerDependencies": {
50
51
  "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
52
+ "@opentelemetry/api": "^1.0.0",
51
53
  "svelte": "^4.0.0 || ^5.0.0-next.0",
52
54
  "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
53
55
  },
56
+ "peerDependenciesMeta": {
57
+ "@opentelemetry/api": {
58
+ "optional": true
59
+ }
60
+ },
54
61
  "bin": {
55
62
  "svelte-kit": "svelte-kit.js"
56
63
  },
@@ -72,6 +79,10 @@
72
79
  "types": "./types/index.d.ts",
73
80
  "import": "./src/exports/internal/index.js"
74
81
  },
82
+ "./internal/server": {
83
+ "types": "./types/index.d.ts",
84
+ "import": "./src/exports/internal/server.js"
85
+ },
75
86
  "./node": {
76
87
  "types": "./types/index.d.ts",
77
88
  "import": "./src/exports/node/index.js"
@@ -1,6 +1,10 @@
1
+ /** @import { Builder } from '@sveltejs/kit' */
2
+ /** @import { ResolvedConfig } from 'vite' */
3
+ /** @import { RouteDefinition } from '@sveltejs/kit' */
4
+ /** @import { RouteData, ValidatedConfig, BuildData, ServerMetadata, ServerMetadataRoute, Prerendered, PrerenderMap, Logger } from 'types' */
1
5
  import colors from 'kleur';
2
6
  import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs';
3
- import { extname, resolve } from 'node:path';
7
+ import { extname, resolve, join, dirname, relative } from 'node:path';
4
8
  import { pipeline } from 'node:stream';
5
9
  import { promisify } from 'node:util';
6
10
  import zlib from 'node:zlib';
@@ -12,6 +16,7 @@ import generate_fallback from '../postbuild/fallback.js';
12
16
  import { write } from '../sync/utils.js';
13
17
  import { list_files } from '../utils.js';
14
18
  import { find_server_assets } from '../generate_manifest/find_server_assets.js';
19
+ import { reserved } from '../env.js';
15
20
 
16
21
  const pipe = promisify(pipeline);
17
22
  const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm'];
@@ -19,16 +24,16 @@ const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.w
19
24
  /**
20
25
  * Creates the Builder which is passed to adapters for building the application.
21
26
  * @param {{
22
- * config: import('types').ValidatedConfig;
23
- * build_data: import('types').BuildData;
24
- * server_metadata: import('types').ServerMetadata;
25
- * route_data: import('types').RouteData[];
26
- * prerendered: import('types').Prerendered;
27
- * prerender_map: import('types').PrerenderMap;
28
- * log: import('types').Logger;
29
- * vite_config: import('vite').ResolvedConfig;
27
+ * config: ValidatedConfig;
28
+ * build_data: BuildData;
29
+ * server_metadata: ServerMetadata;
30
+ * route_data: RouteData[];
31
+ * prerendered: Prerendered;
32
+ * prerender_map: PrerenderMap;
33
+ * log: Logger;
34
+ * vite_config: ResolvedConfig;
30
35
  * }} opts
31
- * @returns {import('@sveltejs/kit').Builder}
36
+ * @returns {Builder}
32
37
  */
33
38
  export function create_builder({
34
39
  config,
@@ -40,7 +45,7 @@ export function create_builder({
40
45
  log,
41
46
  vite_config
42
47
  }) {
43
- /** @type {Map<import('@sveltejs/kit').RouteDefinition, import('types').RouteData>} */
48
+ /** @type {Map<RouteDefinition, RouteData>} */
44
49
  const lookup = new Map();
45
50
 
46
51
  /**
@@ -48,11 +53,11 @@ export function create_builder({
48
53
  * we expose a stable type that adapters can use to group/filter routes
49
54
  */
50
55
  const routes = route_data.map((route) => {
51
- const { config, methods, page, api } = /** @type {import('types').ServerMetadataRoute} */ (
56
+ const { config, methods, page, api } = /** @type {ServerMetadataRoute} */ (
52
57
  server_metadata.routes.get(route.id)
53
58
  );
54
59
 
55
- /** @type {import('@sveltejs/kit').RouteDefinition} */
60
+ /** @type {RouteDefinition} */
56
61
  const facade = {
57
62
  id: route.id,
58
63
  api,
@@ -229,6 +234,53 @@ export function create_builder({
229
234
 
230
235
  writeServer(dest) {
231
236
  return copy(`${config.kit.outDir}/output/server`, dest);
237
+ },
238
+
239
+ hasServerInstrumentationFile() {
240
+ return existsSync(`${config.kit.outDir}/output/server/instrumentation.server.js`);
241
+ },
242
+
243
+ instrument({
244
+ entrypoint,
245
+ instrumentation,
246
+ start = join(dirname(entrypoint), 'start.js'),
247
+ module = {
248
+ exports: ['default']
249
+ }
250
+ }) {
251
+ if (!existsSync(instrumentation)) {
252
+ throw new Error(
253
+ `Instrumentation file ${instrumentation} not found. This is probably a bug in your adapter.`
254
+ );
255
+ }
256
+ if (!existsSync(entrypoint)) {
257
+ throw new Error(
258
+ `Entrypoint file ${entrypoint} not found. This is probably a bug in your adapter.`
259
+ );
260
+ }
261
+
262
+ copy(entrypoint, start);
263
+ if (existsSync(`${entrypoint}.map`)) {
264
+ copy(`${entrypoint}.map`, `${start}.map`);
265
+ }
266
+
267
+ const relative_instrumentation = relative(dirname(entrypoint), instrumentation);
268
+ const relative_start = relative(dirname(entrypoint), start);
269
+
270
+ const facade =
271
+ 'generateText' in module
272
+ ? module.generateText({
273
+ instrumentation: relative_instrumentation,
274
+ start: relative_start
275
+ })
276
+ : create_instrumentation_facade({
277
+ instrumentation: relative_instrumentation,
278
+ start: relative_start,
279
+ exports: module.exports
280
+ });
281
+
282
+ rimraf(entrypoint);
283
+ write(entrypoint, facade);
232
284
  }
233
285
  };
234
286
  }
@@ -254,3 +306,60 @@ async function compress_file(file, format = 'gz') {
254
306
 
255
307
  await pipe(source, compress, destination);
256
308
  }
309
+
310
+ /**
311
+ * Given a list of exports, generate a facade that:
312
+ * - Imports the instrumentation file
313
+ * - Imports `exports` from the entrypoint (dynamically, if `tla` is true)
314
+ * - Re-exports `exports` from the entrypoint
315
+ *
316
+ * `default` receives special treatment: It will be imported as `default` and exported with `export default`.
317
+ *
318
+ * @param {{ instrumentation: string; start: string; exports: string[] }} opts
319
+ * @returns {string}
320
+ */
321
+ function create_instrumentation_facade({ instrumentation, start, exports }) {
322
+ const import_instrumentation = `import './${instrumentation}';`;
323
+
324
+ let alias_index = 0;
325
+ const aliases = new Map();
326
+
327
+ for (const name of exports.filter((name) => reserved.has(name))) {
328
+ /*
329
+ * you can do evil things like `export { c as class }`.
330
+ * in order to import these, you need to alias them, and then un-alias them when re-exporting
331
+ * this map will allow us to generate the following:
332
+ * import { class as _1 } from 'entrypoint';
333
+ * export { _1 as class };
334
+ */
335
+ let alias = `_${alias_index++}`;
336
+ while (exports.includes(alias)) {
337
+ alias = `_${alias_index++}`;
338
+ }
339
+
340
+ aliases.set(name, alias);
341
+ }
342
+
343
+ const import_statements = [];
344
+ const export_statements = [];
345
+
346
+ for (const name of exports) {
347
+ const alias = aliases.get(name);
348
+ if (alias) {
349
+ import_statements.push(`${name}: ${alias}`);
350
+ export_statements.push(`${alias} as ${name}`);
351
+ } else {
352
+ import_statements.push(`${name}`);
353
+ export_statements.push(`${name}`);
354
+ }
355
+ }
356
+
357
+ const entrypoint_facade = [
358
+ `const { ${import_statements.join(', ')} } = await import('./${start}');`,
359
+ export_statements.length > 0 ? `export { ${export_statements.join(', ')} };` : ''
360
+ ]
361
+ .filter(Boolean)
362
+ .join('\n');
363
+
364
+ return `${import_instrumentation}\n${entrypoint_facade}`;
365
+ }
@@ -120,6 +120,12 @@ const options = object(
120
120
  }),
121
121
 
122
122
  experimental: object({
123
+ tracing: object({
124
+ server: boolean(false)
125
+ }),
126
+ instrumentation: object({
127
+ server: boolean(false)
128
+ }),
123
129
  remoteFunctions: boolean(false)
124
130
  }),
125
131
 
@@ -9,14 +9,13 @@ import { write_non_ambient } from './write_non_ambient.js';
9
9
  import { write_server } from './write_server.js';
10
10
 
11
11
  /**
12
- * Initialize SvelteKit's generated files.
12
+ * Initialize SvelteKit's generated files that only depend on the config and mode.
13
13
  * @param {import('types').ValidatedConfig} config
14
14
  * @param {string} mode
15
15
  */
16
16
  export function init(config, mode) {
17
17
  write_tsconfig(config.kit);
18
18
  write_ambient(config.kit, mode);
19
- write_non_ambient(config.kit);
20
19
  }
21
20
 
22
21
  /**
@@ -32,6 +31,7 @@ export function create(config) {
32
31
  write_server(config, output);
33
32
  write_root(manifest_data, output);
34
33
  write_all_types(config, manifest_data);
34
+ write_non_ambient(config.kit, manifest_data);
35
35
 
36
36
  return { manifest_data };
37
37
  }
@@ -67,6 +67,7 @@ export function all_types(config, mode) {
67
67
  init(config, mode);
68
68
  const manifest_data = create_manifest_data({ config });
69
69
  write_all_types(config, manifest_data);
70
+ write_non_ambient(config.kit, manifest_data);
70
71
  }
71
72
 
72
73
  /**
@@ -1,6 +1,17 @@
1
1
  import path from 'node:path';
2
2
  import { GENERATED_COMMENT } from '../../constants.js';
3
3
  import { write_if_changed } from './utils.js';
4
+ import { s } from '../../utils/misc.js';
5
+ import { get_route_segments } from '../../utils/routing.js';
6
+
7
+ const replace_optional_params = (/** @type {string} */ id) =>
8
+ id.replace(/\/\[\[[^\]]+\]\]/g, '${string}');
9
+ const replace_required_params = (/** @type {string} */ id) =>
10
+ id.replace(/\/\[[^\]]+\]/g, '/${string}');
11
+ /** Convert route ID to pathname by removing layout groups */
12
+ const remove_group_segments = (/** @type {string} */ id) => {
13
+ return '/' + get_route_segments(id).join('/');
14
+ };
4
15
 
5
16
  // `declare module "svelte/elements"` needs to happen in a non-ambient module, and dts-buddy generates one big ambient module,
6
17
  // so we can't add it there - therefore generate the typings ourselves here.
@@ -33,10 +44,75 @@ declare module "svelte/elements" {
33
44
  export {};
34
45
  `;
35
46
 
47
+ /**
48
+ * Generate app types interface extension
49
+ * @param {import('types').ManifestData} manifest_data
50
+ */
51
+ function generate_app_types(manifest_data) {
52
+ /** @type {Set<string>} */
53
+ const pathnames = new Set();
54
+
55
+ /** @type {string[]} */
56
+ const dynamic_routes = [];
57
+
58
+ /** @type {string[]} */
59
+ const layouts = [];
60
+
61
+ for (const route of manifest_data.routes) {
62
+ if (route.params.length > 0) {
63
+ const params = route.params.map((p) => `${p.name}${p.optional ? '?:' : ':'} string`);
64
+ const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
65
+
66
+ dynamic_routes.push(route_type);
67
+
68
+ const pathname = remove_group_segments(route.id);
69
+ pathnames.add(`\`${replace_required_params(replace_optional_params(pathname))}\` & {}`);
70
+ } else {
71
+ const pathname = remove_group_segments(route.id);
72
+ pathnames.add(s(pathname));
73
+ }
74
+
75
+ /** @type {Map<string, boolean>} */
76
+ const child_params = new Map(route.params.map((p) => [p.name, p.optional]));
77
+
78
+ for (const child of manifest_data.routes.filter((r) => r.id.startsWith(route.id))) {
79
+ for (const p of child.params) {
80
+ if (!child_params.has(p.name)) {
81
+ child_params.set(p.name, true); // always optional
82
+ }
83
+ }
84
+ }
85
+
86
+ const layout_params = Array.from(child_params)
87
+ .map(([name, optional]) => `${name}${optional ? '?:' : ':'} string`)
88
+ .join('; ');
89
+
90
+ const layout_type = `${s(route.id)}: ${layout_params.length > 0 ? `{ ${layout_params} }` : 'Record<string, never>'}`;
91
+ layouts.push(layout_type);
92
+ }
93
+
94
+ return [
95
+ 'declare module "$app/types" {',
96
+ '\texport interface AppTypes {',
97
+ `\t\tRouteId(): ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
98
+ `\t\tRouteParams(): {\n\t\t\t${dynamic_routes.join(';\n\t\t\t')}\n\t\t};`,
99
+ `\t\tLayoutParams(): {\n\t\t\t${layouts.join(';\n\t\t\t')}\n\t\t};`,
100
+ `\t\tPathname(): ${Array.from(pathnames).join(' | ')};`,
101
+ '\t\tResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes[\'Pathname\']>}`;',
102
+ `\t\tAsset(): ${manifest_data.assets.map((asset) => s('/' + asset.file)).join(' | ') || 'never'};`,
103
+ '\t}',
104
+ '}'
105
+ ].join('\n');
106
+ }
107
+
36
108
  /**
37
109
  * Writes non-ambient declarations to the output directory
38
110
  * @param {import('types').ValidatedKitConfig} config
111
+ * @param {import('types').ManifestData} manifest_data
39
112
  */
40
- export function write_non_ambient(config) {
41
- write_if_changed(path.join(config.outDir, 'non-ambient.d.ts'), template);
113
+ export function write_non_ambient(config, manifest_data) {
114
+ const app_types = generate_app_types(manifest_data);
115
+ const content = [template, app_types].join('\n\n');
116
+
117
+ write_if_changed(path.join(config.outDir, 'non-ambient.d.ts'), content);
42
118
  }
@@ -5,19 +5,8 @@ import MagicString from 'magic-string';
5
5
  import { posixify, rimraf, walk } from '../../../utils/filesystem.js';
6
6
  import { compact } from '../../../utils/array.js';
7
7
  import { ts } from '../ts.js';
8
- import { s } from '../../../utils/misc.js';
9
- import { get_route_segments } from '../../../utils/routing.js';
10
-
11
8
  const remove_relative_parent_traversals = (/** @type {string} */ path) =>
12
9
  path.replace(/\.\.\//g, '');
13
- const replace_optional_params = (/** @type {string} */ id) =>
14
- id.replace(/\/\[\[[^\]]+\]\]/g, '${string}');
15
- const replace_required_params = (/** @type {string} */ id) =>
16
- id.replace(/\/\[[^\]]+\]/g, '/${string}');
17
- /** Convert route ID to pathname by removing layout groups */
18
- const remove_group_segments = (/** @type {string} */ id) => {
19
- return '/' + get_route_segments(id).join('/');
20
- };
21
10
  const is_whitespace = (/** @type {string} */ char) => /\s/.test(char);
22
11
 
23
12
  /**
@@ -65,67 +54,6 @@ export function write_all_types(config, manifest_data) {
65
54
  }
66
55
  }
67
56
 
68
- /** @type {Set<string>} */
69
- const pathnames = new Set();
70
-
71
- /** @type {string[]} */
72
- const dynamic_routes = [];
73
-
74
- /** @type {string[]} */
75
- const layouts = [];
76
-
77
- for (const route of manifest_data.routes) {
78
- if (route.params.length > 0) {
79
- const params = route.params.map((p) => `${p.name}${p.optional ? '?:' : ':'} string`);
80
- const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
81
-
82
- dynamic_routes.push(route_type);
83
-
84
- const pathname = remove_group_segments(route.id);
85
- pathnames.add(`\`${replace_required_params(replace_optional_params(pathname))}\` & {}`);
86
- } else {
87
- const pathname = remove_group_segments(route.id);
88
- pathnames.add(s(pathname));
89
- }
90
-
91
- /** @type {Map<string, boolean>} */
92
- const child_params = new Map(route.params.map((p) => [p.name, p.optional]));
93
-
94
- for (const child of manifest_data.routes.filter((r) => r.id.startsWith(route.id))) {
95
- for (const p of child.params) {
96
- if (!child_params.has(p.name)) {
97
- child_params.set(p.name, true); // always optional
98
- }
99
- }
100
- }
101
-
102
- const layout_params = Array.from(child_params)
103
- .map(([name, optional]) => `${name}${optional ? '?:' : ':'} string`)
104
- .join('; ');
105
-
106
- const layout_type = `${s(route.id)}: ${layout_params.length > 0 ? `{ ${layout_params} }` : 'undefined'}`;
107
- layouts.push(layout_type);
108
- }
109
-
110
- try {
111
- fs.mkdirSync(types_dir, { recursive: true });
112
- } catch {}
113
-
114
- fs.writeFileSync(
115
- `${types_dir}/index.d.ts`,
116
- [
117
- `type DynamicRoutes = {\n\t${dynamic_routes.join(';\n\t')}\n};`,
118
- `type Layouts = {\n\t${layouts.join(';\n\t')}\n};`,
119
- // we enumerate these rather than doing `keyof Routes` so that the list is visible on hover
120
- `export type RouteId = ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
121
- 'export type RouteParams<T extends RouteId> = T extends keyof DynamicRoutes ? DynamicRoutes[T] : Record<string, never>;',
122
- 'export type LayoutParams<T extends RouteId> = Layouts[T] | Record<string, never>;',
123
- `export type Pathname = ${Array.from(pathnames).join(' | ')};`,
124
- 'export type ResolvedPathname = `${"" | `/${string}`}${Pathname}`;',
125
- `export type Asset = ${manifest_data.assets.map((asset) => s('/' + asset.file)).join(' | ') || 'never'};`
126
- ].join('\n\n')
127
- );
128
-
129
57
  // Read/write meta data on each invocation, not once per node process,
130
58
  // it could be invoked by another process in the meantime.
131
59
  const meta_data_file = `${types_dir}/route_meta_data.json`;
@@ -1,3 +1,11 @@
1
+ /** @import { Handle, RequestEvent, ResolveOptions } from '@sveltejs/kit' */
2
+ /** @import { MaybePromise } from 'types' */
3
+ import {
4
+ merge_tracing,
5
+ get_request_store,
6
+ with_request_store
7
+ } from '@sveltejs/kit/internal/server';
8
+
1
9
  /**
2
10
  * A helper function for sequencing multiple `handle` calls in a middleware-like manner.
3
11
  * The behavior for the `handle` options is as follows:
@@ -66,56 +74,70 @@
66
74
  * first post-processing
67
75
  * ```
68
76
  *
69
- * @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions
70
- * @returns {import('@sveltejs/kit').Handle}
77
+ * @param {...Handle} handlers The chain of `handle` functions
78
+ * @returns {Handle}
71
79
  */
72
80
  export function sequence(...handlers) {
73
81
  const length = handlers.length;
74
82
  if (!length) return ({ event, resolve }) => resolve(event);
75
83
 
76
84
  return ({ event, resolve }) => {
85
+ const { state } = get_request_store();
77
86
  return apply_handle(0, event, {});
78
87
 
79
88
  /**
80
89
  * @param {number} i
81
- * @param {import('@sveltejs/kit').RequestEvent} event
82
- * @param {import('@sveltejs/kit').ResolveOptions | undefined} parent_options
83
- * @returns {import('types').MaybePromise<Response>}
90
+ * @param {RequestEvent} event
91
+ * @param {ResolveOptions | undefined} parent_options
92
+ * @returns {MaybePromise<Response>}
84
93
  */
85
94
  function apply_handle(i, event, parent_options) {
86
95
  const handle = handlers[i];
87
96
 
88
- return handle({
89
- event,
90
- resolve: (event, options) => {
91
- /** @type {import('@sveltejs/kit').ResolveOptions['transformPageChunk']} */
92
- const transformPageChunk = async ({ html, done }) => {
93
- if (options?.transformPageChunk) {
94
- html = (await options.transformPageChunk({ html, done })) ?? '';
95
- }
97
+ return state.tracing.record_span({
98
+ name: `sveltekit.handle.sequenced.${handle.name ? handle.name : i}`,
99
+ attributes: {},
100
+ fn: async (current) => {
101
+ const traced_event = merge_tracing(event, current);
102
+ return await with_request_store({ event: traced_event, state }, () =>
103
+ handle({
104
+ event: traced_event,
105
+ resolve: (event, options) => {
106
+ /** @type {ResolveOptions['transformPageChunk']} */
107
+ const transformPageChunk = async ({ html, done }) => {
108
+ if (options?.transformPageChunk) {
109
+ html = (await options.transformPageChunk({ html, done })) ?? '';
110
+ }
96
111
 
97
- if (parent_options?.transformPageChunk) {
98
- html = (await parent_options.transformPageChunk({ html, done })) ?? '';
99
- }
112
+ if (parent_options?.transformPageChunk) {
113
+ html = (await parent_options.transformPageChunk({ html, done })) ?? '';
114
+ }
100
115
 
101
- return html;
102
- };
116
+ return html;
117
+ };
103
118
 
104
- /** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */
105
- const filterSerializedResponseHeaders =
106
- parent_options?.filterSerializedResponseHeaders ??
107
- options?.filterSerializedResponseHeaders;
119
+ /** @type {ResolveOptions['filterSerializedResponseHeaders']} */
120
+ const filterSerializedResponseHeaders =
121
+ parent_options?.filterSerializedResponseHeaders ??
122
+ options?.filterSerializedResponseHeaders;
108
123
 
109
- /** @type {import('@sveltejs/kit').ResolveOptions['preload']} */
110
- const preload = parent_options?.preload ?? options?.preload;
124
+ /** @type {ResolveOptions['preload']} */
125
+ const preload = parent_options?.preload ?? options?.preload;
111
126
 
112
- return i < length - 1
113
- ? apply_handle(i + 1, event, {
114
- transformPageChunk,
115
- filterSerializedResponseHeaders,
116
- preload
117
- })
118
- : resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
127
+ return i < length - 1
128
+ ? apply_handle(i + 1, event, {
129
+ transformPageChunk,
130
+ filterSerializedResponseHeaders,
131
+ preload
132
+ })
133
+ : resolve(event, {
134
+ transformPageChunk,
135
+ filterSerializedResponseHeaders,
136
+ preload
137
+ });
138
+ }
139
+ })
140
+ );
119
141
  }
120
142
  });
121
143
  }
@@ -1,9 +1,11 @@
1
1
  /** @import { RequestEvent } from '@sveltejs/kit' */
2
+ /** @import { RequestStore } from 'types' */
3
+ /** @import { AsyncLocalStorage } from 'node:async_hooks' */
2
4
 
3
- /** @type {RequestEvent | null} */
4
- let request_event = null;
5
+ /** @type {RequestStore | null} */
6
+ let sync_store = null;
5
7
 
6
- /** @type {import('node:async_hooks').AsyncLocalStorage<RequestEvent | null>} */
8
+ /** @type {AsyncLocalStorage<RequestStore | null> | null} */
7
9
  let als;
8
10
 
9
11
  import('node:async_hooks')
@@ -19,10 +21,11 @@ import('node:async_hooks')
19
21
  *
20
22
  * In environments without [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage), this must be called synchronously (i.e. not after an `await`).
21
23
  * @since 2.20.0
24
+ *
22
25
  * @returns {RequestEvent}
23
26
  */
24
27
  export function getRequestEvent() {
25
- const event = request_event ?? als?.getStore();
28
+ const event = try_get_request_store()?.event;
26
29
 
27
30
  if (!event) {
28
31
  let message =
@@ -39,16 +42,28 @@ export function getRequestEvent() {
39
42
  return event;
40
43
  }
41
44
 
45
+ export function get_request_store() {
46
+ const result = try_get_request_store();
47
+ if (!result) {
48
+ throw new Error('Could not get the request store. This is an internal error.');
49
+ }
50
+ return result;
51
+ }
52
+
53
+ export function try_get_request_store() {
54
+ return sync_store ?? als?.getStore() ?? null;
55
+ }
56
+
42
57
  /**
43
58
  * @template T
44
- * @param {RequestEvent | null} event
59
+ * @param {RequestStore | null} store
45
60
  * @param {() => T} fn
46
61
  */
47
- export function with_event(event, fn) {
62
+ export function with_request_store(store, fn) {
48
63
  try {
49
- request_event = event;
50
- return als ? als.run(event, fn) : fn();
64
+ sync_store = store;
65
+ return als ? als.run(store, fn) : fn();
51
66
  } finally {
52
- request_event = null;
67
+ sync_store = null;
53
68
  }
54
69
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @template {{ tracing: { enabled: boolean, root: import('@opentelemetry/api').Span, current: import('@opentelemetry/api').Span } }} T
3
+ * @param {T} event_like
4
+ * @param {import('@opentelemetry/api').Span} current
5
+ * @returns {T}
6
+ */
7
+ export function merge_tracing(event_like, current) {
8
+ return {
9
+ ...event_like,
10
+ tracing: {
11
+ ...event_like.tracing,
12
+ current
13
+ }
14
+ };
15
+ }
16
+
17
+ export {
18
+ with_request_store,
19
+ getRequestEvent,
20
+ get_request_store,
21
+ try_get_request_store
22
+ } from './event.js';