@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.
- package/package.json +12 -1
- package/src/core/adapt/builder.js +122 -13
- package/src/core/config/options.js +6 -0
- package/src/core/sync/sync.js +3 -2
- package/src/core/sync/write_non_ambient.js +78 -2
- package/src/core/sync/write_types/index.js +0 -72
- package/src/exports/hooks/sequence.js +53 -31
- package/src/{runtime/app/server → exports/internal}/event.js +24 -9
- package/src/exports/internal/server.js +22 -0
- package/src/exports/public.d.ts +117 -6
- package/src/exports/vite/dev/index.js +8 -0
- package/src/exports/vite/index.js +30 -3
- package/src/exports/vite/utils.js +44 -0
- package/src/runtime/app/paths/index.js +3 -1
- package/src/runtime/app/paths/types.d.ts +0 -1
- package/src/runtime/app/server/index.js +1 -1
- package/src/runtime/app/server/remote/command.js +4 -5
- package/src/runtime/app/server/remote/form.js +5 -7
- package/src/runtime/app/server/remote/prerender.js +5 -7
- package/src/runtime/app/server/remote/query.js +6 -8
- package/src/runtime/app/server/remote/shared.js +9 -13
- package/src/runtime/client/client.js +9 -14
- package/src/runtime/server/data/index.js +10 -5
- package/src/runtime/server/endpoint.js +4 -3
- package/src/runtime/server/page/actions.js +55 -24
- package/src/runtime/server/page/index.js +22 -5
- package/src/runtime/server/page/load_data.js +131 -121
- package/src/runtime/server/page/render.js +15 -7
- package/src/runtime/server/page/respond_with_error.js +7 -2
- package/src/runtime/server/remote.js +59 -13
- package/src/runtime/server/respond.js +110 -34
- package/src/runtime/server/utils.js +20 -5
- package/src/runtime/shared.js +20 -0
- package/src/runtime/telemetry/noop.js +81 -0
- package/src/runtime/telemetry/otel.js +21 -0
- package/src/runtime/telemetry/record_span.js +65 -0
- package/src/types/ambient.d.ts +54 -0
- package/src/types/global-private.d.ts +2 -0
- package/src/types/internal.d.ts +28 -0
- package/src/types/synthetic/$env+dynamic+private.md +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +171 -4
- package/types/index.d.ts.map +2 -2
- package/src/runtime/server/event-state.js +0 -40
|
@@ -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 {
|
|
38
|
-
import {
|
|
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
|
|
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
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
|
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
|
+
}
|
package/src/runtime/shared.js
CHANGED
|
@@ -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
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** @import { Tracer, Span, SpanContext } from '@opentelemetry/api' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracer implementation that does nothing (null object).
|
|
5
|
+
* @type {Tracer}
|
|
6
|
+
*/
|
|
7
|
+
export const noop_tracer = {
|
|
8
|
+
/**
|
|
9
|
+
* @returns {Span}
|
|
10
|
+
*/
|
|
11
|
+
startSpan() {
|
|
12
|
+
return noop_span;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {unknown} _name
|
|
17
|
+
* @param {unknown} arg_1
|
|
18
|
+
* @param {unknown} [arg_2]
|
|
19
|
+
* @param {Function} [arg_3]
|
|
20
|
+
* @returns {unknown}
|
|
21
|
+
*/
|
|
22
|
+
startActiveSpan(_name, arg_1, arg_2, arg_3) {
|
|
23
|
+
if (typeof arg_1 === 'function') {
|
|
24
|
+
return arg_1(noop_span);
|
|
25
|
+
}
|
|
26
|
+
if (typeof arg_2 === 'function') {
|
|
27
|
+
return arg_2(noop_span);
|
|
28
|
+
}
|
|
29
|
+
if (typeof arg_3 === 'function') {
|
|
30
|
+
return arg_3(noop_span);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @type {Span}
|
|
37
|
+
*/
|
|
38
|
+
export const noop_span = {
|
|
39
|
+
spanContext() {
|
|
40
|
+
return noop_span_context;
|
|
41
|
+
},
|
|
42
|
+
setAttribute() {
|
|
43
|
+
return this;
|
|
44
|
+
},
|
|
45
|
+
setAttributes() {
|
|
46
|
+
return this;
|
|
47
|
+
},
|
|
48
|
+
addEvent() {
|
|
49
|
+
return this;
|
|
50
|
+
},
|
|
51
|
+
setStatus() {
|
|
52
|
+
return this;
|
|
53
|
+
},
|
|
54
|
+
updateName() {
|
|
55
|
+
return this;
|
|
56
|
+
},
|
|
57
|
+
end() {
|
|
58
|
+
return this;
|
|
59
|
+
},
|
|
60
|
+
isRecording() {
|
|
61
|
+
return false;
|
|
62
|
+
},
|
|
63
|
+
recordException() {
|
|
64
|
+
return this;
|
|
65
|
+
},
|
|
66
|
+
addLink() {
|
|
67
|
+
return this;
|
|
68
|
+
},
|
|
69
|
+
addLinks() {
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {SpanContext}
|
|
76
|
+
*/
|
|
77
|
+
const noop_span_context = {
|
|
78
|
+
traceId: '',
|
|
79
|
+
spanId: '',
|
|
80
|
+
traceFlags: 0
|
|
81
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @import { Tracer, SpanStatusCode, PropagationAPI, ContextAPI } from '@opentelemetry/api' */
|
|
2
|
+
|
|
3
|
+
/** @type {Promise<{ tracer: Tracer, SpanStatusCode: typeof SpanStatusCode, propagation: PropagationAPI, context: ContextAPI }> | null} */
|
|
4
|
+
export let otel = null;
|
|
5
|
+
|
|
6
|
+
if (__SVELTEKIT_SERVER_TRACING_ENABLED__) {
|
|
7
|
+
otel = import('@opentelemetry/api')
|
|
8
|
+
.then((module) => {
|
|
9
|
+
return {
|
|
10
|
+
tracer: module.trace.getTracer('sveltekit'),
|
|
11
|
+
propagation: module.propagation,
|
|
12
|
+
context: module.context,
|
|
13
|
+
SpanStatusCode: module.SpanStatusCode
|
|
14
|
+
};
|
|
15
|
+
})
|
|
16
|
+
.catch(() => {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'Tracing is enabled (see `config.kit.experimental.instrumentation.server` in your svelte.config.js), but `@opentelemetry/api` is not available. Have you installed it?'
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** @import { RecordSpan } from 'types' */
|
|
2
|
+
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
3
|
+
import { noop_span } from './noop.js';
|
|
4
|
+
import { otel } from './otel.js';
|
|
5
|
+
|
|
6
|
+
/** @type {RecordSpan} */
|
|
7
|
+
export async function record_span({ name, attributes, fn }) {
|
|
8
|
+
if (otel === null) {
|
|
9
|
+
return fn(noop_span);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { SpanStatusCode, tracer } = await otel;
|
|
13
|
+
|
|
14
|
+
return tracer.startActiveSpan(name, { attributes }, async (span) => {
|
|
15
|
+
try {
|
|
16
|
+
return await fn(span);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error instanceof HttpError) {
|
|
19
|
+
span.setAttributes({
|
|
20
|
+
[`${name}.result.type`]: 'known_error',
|
|
21
|
+
[`${name}.result.status`]: error.status,
|
|
22
|
+
[`${name}.result.message`]: error.body.message
|
|
23
|
+
});
|
|
24
|
+
if (error.status >= 500) {
|
|
25
|
+
span.recordException({
|
|
26
|
+
name: 'HttpError',
|
|
27
|
+
message: error.body.message
|
|
28
|
+
});
|
|
29
|
+
span.setStatus({
|
|
30
|
+
code: SpanStatusCode.ERROR,
|
|
31
|
+
message: error.body.message
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
} else if (error instanceof Redirect) {
|
|
35
|
+
span.setAttributes({
|
|
36
|
+
[`${name}.result.type`]: 'redirect',
|
|
37
|
+
[`${name}.result.status`]: error.status,
|
|
38
|
+
[`${name}.result.location`]: error.location
|
|
39
|
+
});
|
|
40
|
+
} else if (error instanceof Error) {
|
|
41
|
+
span.setAttributes({
|
|
42
|
+
[`${name}.result.type`]: 'unknown_error'
|
|
43
|
+
});
|
|
44
|
+
span.recordException({
|
|
45
|
+
name: error.name,
|
|
46
|
+
message: error.message,
|
|
47
|
+
stack: error.stack
|
|
48
|
+
});
|
|
49
|
+
span.setStatus({
|
|
50
|
+
code: SpanStatusCode.ERROR,
|
|
51
|
+
message: error.message
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
span.setAttributes({
|
|
55
|
+
[`${name}.result.type`]: 'unknown_error'
|
|
56
|
+
});
|
|
57
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw error;
|
|
61
|
+
} finally {
|
|
62
|
+
span.end();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
package/src/types/ambient.d.ts
CHANGED
|
@@ -79,3 +79,57 @@ declare module '$service-worker' {
|
|
|
79
79
|
*/
|
|
80
80
|
export const version: string;
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* This module contains generated types for the routes in your app.
|
|
85
|
+
*/
|
|
86
|
+
declare module '$app/types' {
|
|
87
|
+
/**
|
|
88
|
+
* Interface for all generated app types. This gets extended via declaration merging. DO NOT USE THIS INTERFACE DIRECTLY.
|
|
89
|
+
*/
|
|
90
|
+
export interface AppTypes {
|
|
91
|
+
// These are all functions so that we can leverage function overloads to get the correct type.
|
|
92
|
+
// Using the return types directly would error with a "not the same type" error.
|
|
93
|
+
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces
|
|
94
|
+
RouteId(): string;
|
|
95
|
+
RouteParams(): Record<string, Record<string, string>>;
|
|
96
|
+
LayoutParams(): Record<string, Record<string, string>>;
|
|
97
|
+
Pathname(): string;
|
|
98
|
+
ResolvedPathname(): string;
|
|
99
|
+
Asset(): string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A union of all the route IDs in your app. Used for `page.route.id` and `event.route.id`.
|
|
104
|
+
*/
|
|
105
|
+
export type RouteId = ReturnType<AppTypes['RouteId']>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* A utility for getting the parameters associated with a given route.
|
|
109
|
+
*/
|
|
110
|
+
export type RouteParams<T extends RouteId> = T extends keyof ReturnType<AppTypes['RouteParams']>
|
|
111
|
+
? ReturnType<AppTypes['RouteParams']>[T]
|
|
112
|
+
: Record<string, never>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route.
|
|
116
|
+
*/
|
|
117
|
+
export type LayoutParams<T extends RouteId> = T extends keyof ReturnType<AppTypes['LayoutParams']>
|
|
118
|
+
? ReturnType<AppTypes['LayoutParams']>[T]
|
|
119
|
+
: Record<string, never>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A union of all valid pathnames in your app.
|
|
123
|
+
*/
|
|
124
|
+
export type Pathname = ReturnType<AppTypes['Pathname']>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* `Pathname`, but possibly prefixed with a base path. Used for `page.url.pathname`.
|
|
128
|
+
*/
|
|
129
|
+
export type ResolvedPathname = ReturnType<AppTypes['ResolvedPathname']>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A union of all the filenames of assets contained in your `static` directory.
|
|
133
|
+
*/
|
|
134
|
+
export type Asset = ReturnType<AppTypes['Asset']>;
|
|
135
|
+
}
|
|
@@ -4,6 +4,8 @@ declare global {
|
|
|
4
4
|
const __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: number;
|
|
5
5
|
const __SVELTEKIT_DEV__: boolean;
|
|
6
6
|
const __SVELTEKIT_EMBEDDED__: boolean;
|
|
7
|
+
/** True if `config.kit.experimental.instrumentation.server` is `true` */
|
|
8
|
+
const __SVELTEKIT_SERVER_TRACING_ENABLED__: boolean;
|
|
7
9
|
/** true if corresponding config option is set to true */
|
|
8
10
|
const __SVELTEKIT_EXPERIMENTAL__REMOTE_FUNCTIONS__: boolean;
|
|
9
11
|
/** True if `config.kit.router.resolution === 'client'` */
|
package/src/types/internal.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
RequestOptions,
|
|
31
31
|
TrailingSlash
|
|
32
32
|
} from './private.js';
|
|
33
|
+
import { Span } from '@opentelemetry/api';
|
|
33
34
|
|
|
34
35
|
export interface ServerModule {
|
|
35
36
|
Server: typeof InternalServer;
|
|
@@ -567,5 +568,32 @@ export type RemoteInfo =
|
|
|
567
568
|
inputs?: RemotePrerenderInputsGenerator;
|
|
568
569
|
};
|
|
569
570
|
|
|
571
|
+
export type RecordSpan = <T>(options: {
|
|
572
|
+
name: string;
|
|
573
|
+
attributes: Record<string, any>;
|
|
574
|
+
fn: (current: Span) => Promise<T>;
|
|
575
|
+
}) => Promise<T>;
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Internal state associated with the current `RequestEvent`,
|
|
579
|
+
* used for tracking things like remote function calls
|
|
580
|
+
*/
|
|
581
|
+
export interface RequestState {
|
|
582
|
+
prerendering: PrerenderOptions | undefined;
|
|
583
|
+
transport: ServerHooks['transport'];
|
|
584
|
+
handleValidationError: ServerHooks['handleValidationError'];
|
|
585
|
+
tracing: {
|
|
586
|
+
record_span: RecordSpan;
|
|
587
|
+
};
|
|
588
|
+
form_instances?: Map<any, any>;
|
|
589
|
+
remote_data?: Record<string, MaybePromise<any>>;
|
|
590
|
+
refreshes?: Record<string, any>;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export interface RequestStore {
|
|
594
|
+
event: RequestEvent;
|
|
595
|
+
state: RequestState;
|
|
596
|
+
}
|
|
597
|
+
|
|
570
598
|
export * from '../exports/index.js';
|
|
571
599
|
export * from './private.js';
|
|
@@ -9,4 +9,4 @@ import { env } from '$env/dynamic/private';
|
|
|
9
9
|
console.log(env.DEPLOYMENT_SPECIFIC_VARIABLE);
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
> In `dev`, `$env/dynamic` always includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter.
|
|
12
|
+
> [!NOTE] In `dev`, `$env/dynamic` always includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter.
|
package/src/version.js
CHANGED