Skip to content

Commit ae224e2

Browse files
feat: add universal IDs to client nodes
1 parent c4dcfc9 commit ae224e2

38 files changed

+2012
-897
lines changed

packages/kit/src/core/config/index.spec.js

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const get_defaults = (prefix = '') => ({
7676
publicPrefix: 'PUBLIC_',
7777
privatePrefix: ''
7878
},
79+
experimental: {
80+
tracing: undefined
81+
},
7982
files: {
8083
assets: join(prefix, 'static'),
8184
hooks: {
@@ -101,7 +104,6 @@ const get_defaults = (prefix = '') => ({
101104
serviceWorker: {
102105
register: true
103106
},
104-
tracing: false,
105107
typescript: {},
106108
paths: {
107109
base: '',
@@ -407,44 +409,22 @@ test('errors on loading config with incorrect default export', async () => {
407409
});
408410

409411
test('accepts valid tracing values', () => {
410-
// Test boolean values
411412
assert.doesNotThrow(() => {
412413
validate_config({
413414
kit: {
414-
tracing: true
415-
}
416-
});
417-
});
418-
419-
assert.doesNotThrow(() => {
420-
validate_config({
421-
kit: {
422-
tracing: false
423-
}
424-
});
425-
});
426-
427-
// Test string values
428-
assert.doesNotThrow(() => {
429-
validate_config({
430-
kit: {
431-
tracing: 'server'
432-
}
433-
});
434-
});
435-
436-
assert.doesNotThrow(() => {
437-
validate_config({
438-
kit: {
439-
tracing: 'client'
415+
experimental: {
416+
tracing: 'server'
417+
}
440418
}
441419
});
442420
});
443421

444422
assert.doesNotThrow(() => {
445423
validate_config({
446424
kit: {
447-
tracing: undefined
425+
experimental: {
426+
tracing: undefined
427+
}
448428
}
449429
});
450430
});
@@ -454,27 +434,33 @@ test('errors on invalid tracing values', () => {
454434
assert.throws(() => {
455435
validate_config({
456436
kit: {
457-
// @ts-expect-error - given value expected to throw
458-
tracing: 'invalid'
437+
experimental: {
438+
// @ts-expect-error - given value expected to throw
439+
tracing: true
440+
}
459441
}
460442
});
461-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
443+
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);
462444

463445
assert.throws(() => {
464446
validate_config({
465447
kit: {
466-
// @ts-expect-error - given value expected to throw
467-
tracing: 42
448+
experimental: {
449+
// @ts-expect-error - given value expected to throw
450+
tracing: false
451+
}
468452
}
469453
});
470-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
454+
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);
471455

472456
assert.throws(() => {
473457
validate_config({
474458
kit: {
475-
// @ts-expect-error - given value expected to throw
476-
tracing: null
459+
experimental: {
460+
// @ts-expect-error - given value expected to throw
461+
tracing: 'client'
462+
}
477463
}
478464
});
479-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
465+
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);
480466
});

packages/kit/src/core/config/options.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ const options = object(
120120
privatePrefix: string('')
121121
}),
122122

123+
experimental: object({
124+
tracing: validate(undefined, (input, keypath) => {
125+
if (input !== 'server') {
126+
throw new Error(`${keypath} should be undefined or "server"`);
127+
}
128+
return input;
129+
})
130+
}),
131+
123132
files: object({
124133
assets: string('static'),
125134
hooks: object({
@@ -270,12 +279,6 @@ const options = object(
270279
files: fun((filename) => !/\.DS_Store/.test(filename))
271280
}),
272281

273-
tracing: validate(false, (input, keypath) => {
274-
if (typeof input === 'boolean') return input;
275-
if (input === 'server' || input === 'client') return input;
276-
throw new Error(`${keypath} should be true, false, "server", or "client"`);
277-
}),
278-
279282
typescript: object({
280283
config: fun((config) => config)
281284
}),

packages/kit/src/core/sync/write_client_manifest.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
174174
175175
export const hash = ${s(kit.router.type === 'hash')};
176176
177-
export const tracing = ${s(kit.tracing === true || kit.tracing === 'client')};
178-
179177
export const decode = (type, value) => decoders[type](value);
180178
181179
export { default as root } from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';

packages/kit/src/core/sync/write_server.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ import { set_building, set_prerendering } from '__sveltekit/environment';
3333
import { set_assets } from '__sveltekit/paths';
3434
import { set_manifest, set_read_implementation } from '__sveltekit/server';
3535
import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js';
36+
import { get_tracer, enable_tracing } from '${runtime_directory}/telemetry/get_tracer.js';
37+
38+
if (${s(config.kit.experimental.tracing === 'server')}) {
39+
enable_tracing();
40+
}
3641
3742
export const options = {
3843
app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
@@ -60,7 +65,7 @@ export const options = {
6065
.replace(/%sveltekit\.status%/g, '" + status + "')
6166
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
6267
},
63-
tracing: ${config.kit.tracing === true || config.kit.tracing === 'server'},
68+
tracer: get_tracer(),
6469
version_hash: ${s(hash(config.kit.version.name))}
6570
};
6671

packages/kit/src/exports/hooks/sequence.js

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/** @import { Handle, RequestEvent, ResolveOptions } from '@sveltejs/kit' */
2+
/** @import { MaybePromise } from 'types' */
3+
import { with_event } from '../../runtime/app/server/event.js';
4+
import { get_tracer } from '../../runtime/telemetry/get_tracer.js';
5+
import { record_span } from '../../runtime/telemetry/record_span.js';
6+
17
/**
28
* A helper function for sequencing multiple `handle` calls in a middleware-like manner.
39
* The behavior for the `handle` options is as follows:
@@ -66,56 +72,77 @@
6672
* first post-processing
6773
* ```
6874
*
69-
* @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions
70-
* @returns {import('@sveltejs/kit').Handle}
75+
* @param {...Handle} handlers The chain of `handle` functions
76+
* @returns {Handle}
7177
*/
7278
export function sequence(...handlers) {
7379
const length = handlers.length;
7480
if (!length) return ({ event, resolve }) => resolve(event);
7581

76-
return ({ event, resolve }) => {
82+
return async ({ event, resolve }) => {
83+
// there's an assumption here that people aren't doing something insane like sequence(() => {}, sequence(() => {}))
84+
// worst case there is that future spans get a lower-down span as their root span -- the tracing would still work,
85+
// it'd just look a little weird
86+
const { rootSpan } = event.tracing;
87+
const tracer = await get_tracer();
7788
return apply_handle(0, event, {});
7889

7990
/**
8091
* @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>}
92+
* @param {RequestEvent} event
93+
* @param {ResolveOptions | undefined} parent_options
94+
* @returns {MaybePromise<Response>}
8495
*/
8596
function apply_handle(i, event, parent_options) {
8697
const handle = handlers[i];
8798

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-
}
99+
return record_span({
100+
tracer,
101+
name: 'sveltekit.handle.child',
102+
attributes: {
103+
'sveltekit.handle.child.index': i
104+
},
105+
fn: async (span) => {
106+
const traced_event = { ...event, tracing: { rootSpan, currentSpan: span } };
107+
return await with_event(traced_event, () =>
108+
handle({
109+
event: traced_event,
110+
resolve: (event, options) => {
111+
/** @type {ResolveOptions['transformPageChunk']} */
112+
const transformPageChunk = async ({ html, done }) => {
113+
if (options?.transformPageChunk) {
114+
html = (await options.transformPageChunk({ html, done })) ?? '';
115+
}
96116

97-
if (parent_options?.transformPageChunk) {
98-
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
99-
}
117+
if (parent_options?.transformPageChunk) {
118+
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
119+
}
100120

101-
return html;
102-
};
121+
return html;
122+
};
103123

104-
/** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */
105-
const filterSerializedResponseHeaders =
106-
parent_options?.filterSerializedResponseHeaders ??
107-
options?.filterSerializedResponseHeaders;
124+
/** @type {ResolveOptions['filterSerializedResponseHeaders']} */
125+
const filterSerializedResponseHeaders =
126+
parent_options?.filterSerializedResponseHeaders ??
127+
options?.filterSerializedResponseHeaders;
108128

109-
/** @type {import('@sveltejs/kit').ResolveOptions['preload']} */
110-
const preload = parent_options?.preload ?? options?.preload;
129+
/** @type {ResolveOptions['preload']} */
130+
const preload = parent_options?.preload ?? options?.preload;
111131

112-
return i < length - 1
113-
? apply_handle(i + 1, event, {
114-
transformPageChunk,
115-
filterSerializedResponseHeaders,
116-
preload
117-
})
118-
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
132+
return i < length - 1
133+
? apply_handle(i + 1, event, {
134+
transformPageChunk,
135+
filterSerializedResponseHeaders,
136+
preload
137+
})
138+
: resolve(event, {
139+
transformPageChunk,
140+
filterSerializedResponseHeaders,
141+
preload
142+
});
143+
}
144+
})
145+
);
119146
}
120147
});
121148
}

packages/kit/src/exports/hooks/sequence.spec.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { installPolyfills } from '../node/polyfills.js';
44

55
installPolyfills();
66

7+
const dummy_event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({
8+
tracing: { rootSpan: {} }
9+
});
10+
711
test('applies handlers in sequence', async () => {
812
/** @type {string[]} */
913
const order = [];
@@ -29,10 +33,9 @@ test('applies handlers in sequence', async () => {
2933
}
3034
);
3135

32-
const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
3336
const response = new Response();
3437

35-
assert.equal(await handler({ event, resolve: () => response }), response);
38+
assert.equal(await handler({ event: dummy_event, resolve: () => response }), response);
3639
expect(order).toEqual(['1a', '2a', '3a', '3b', '2b', '1b']);
3740
});
3841

@@ -47,9 +50,8 @@ test('uses transformPageChunk option passed to non-terminal handle function', as
4750
async ({ event, resolve }) => resolve(event)
4851
);
4952

50-
const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
5153
const response = await handler({
52-
event,
54+
event: dummy_event,
5355
resolve: async (_event, opts = {}) => {
5456
let html = '';
5557

@@ -84,9 +86,8 @@ test('merges transformPageChunk option', async () => {
8486
}
8587
);
8688

87-
const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
8889
const response = await handler({
89-
event,
90+
event: dummy_event,
9091
resolve: async (_event, opts = {}) => {
9192
let html = '';
9293

@@ -117,9 +118,8 @@ test('uses first defined preload option', async () => {
117118
}
118119
);
119120

120-
const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
121121
const response = await handler({
122-
event,
122+
event: dummy_event,
123123
resolve: (_event, opts = {}) => {
124124
let html = '';
125125

@@ -150,9 +150,8 @@ test('uses first defined filterSerializedResponseHeaders option', async () => {
150150
}
151151
);
152152

153-
const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
154153
const response = await handler({
155-
event,
154+
event: dummy_event,
156155
resolve: (_event, opts = {}) => {
157156
let html = '';
158157

0 commit comments

Comments
 (0)