diff --git a/.changeset/fuzzy-lies-begin.md b/.changeset/fuzzy-lies-begin.md new file mode 100644 index 000000000000..762db0c2af6d --- /dev/null +++ b/.changeset/fuzzy-lies-begin.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: ignore TypeScript errors when `@opentelemetry/api` is not installed diff --git a/packages/kit/scripts/generate-dts.js b/packages/kit/scripts/generate-dts.js index b470d6d63494..6e8807755015 100644 --- a/packages/kit/scripts/generate-dts.js +++ b/packages/kit/scripts/generate-dts.js @@ -15,7 +15,8 @@ await createBundle({ '$app/paths': 'src/runtime/app/paths/public.d.ts', '$app/server': 'src/runtime/app/server/index.js', '$app/state': 'src/runtime/app/state/index.js', - '$app/stores': 'src/runtime/app/stores.js' + '$app/stores': 'src/runtime/app/stores.js', + '@opentelemetry/api': 'src/exports/internal/otel.d.ts' }, include: ['src'] }); diff --git a/packages/kit/src/exports/internal/otel.d.ts b/packages/kit/src/exports/internal/otel.d.ts new file mode 100644 index 000000000000..f1e8f252ef4f --- /dev/null +++ b/packages/kit/src/exports/internal/otel.d.ts @@ -0,0 +1,14 @@ +/* Users may not have this package installed, this prevents errors when using `tsc` without it. */ +/* eslint-disable @typescript-eslint/no-empty-object-type */ + +type _any = any; + +export interface Span extends _any {} + +export interface Tracer extends _any {} + +export interface SpanContext extends _any {} + +export interface PropagationAPI extends _any {} + +export interface ContextAPI extends _any {} diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index 1ef333310326..e59147ed3d81 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -25,6 +25,7 @@ import { LayoutParams as AppLayoutParams, ResolvedPathname } from '$app/types'; + import { Span } from '@opentelemetry/api'; export { PrerenderOption } from '../types/private.js'; diff --git a/packages/kit/src/runtime/telemetry/otel.js b/packages/kit/src/runtime/telemetry/otel.js index 044f6db5a4f9..299865e0c2ed 100644 --- a/packages/kit/src/runtime/telemetry/otel.js +++ b/packages/kit/src/runtime/telemetry/otel.js @@ -1,11 +1,11 @@ -/** @import { Tracer, SpanStatusCode, PropagationAPI, ContextAPI } from '@opentelemetry/api' */ +/** @import { Tracer, PropagationAPI, ContextAPI } from '@opentelemetry/api' */ -/** @type {Promise<{ tracer: Tracer, SpanStatusCode: typeof SpanStatusCode, propagation: PropagationAPI, context: ContextAPI }> | null} */ +/** @type {Promise<{ tracer: Tracer, SpanStatusCode: { ERROR: number }, propagation: PropagationAPI, context: ContextAPI }> | null} */ export let otel = null; if (__SVELTEKIT_SERVER_TRACING_ENABLED__) { otel = import('@opentelemetry/api') - .then((module) => { + .then((/** @type {any} */ module) => { return { tracer: module.trace.getTracer('sveltekit'), propagation: module.propagation, diff --git a/packages/kit/src/runtime/telemetry/record_span.js b/packages/kit/src/runtime/telemetry/record_span.js index eaabb1ea5e4c..5746d43f0b1a 100644 --- a/packages/kit/src/runtime/telemetry/record_span.js +++ b/packages/kit/src/runtime/telemetry/record_span.js @@ -11,55 +11,59 @@ export async function record_span({ name, attributes, fn }) { const { SpanStatusCode, tracer } = await otel; - return tracer.startActiveSpan(name, { attributes }, async (span) => { - try { - return await fn(span); - } catch (error) { - if (error instanceof HttpError) { - span.setAttributes({ - [`${name}.result.type`]: 'known_error', - [`${name}.result.status`]: error.status, - [`${name}.result.message`]: error.body.message - }); - if (error.status >= 500) { + return tracer.startActiveSpan( + name, + { attributes }, + async (/** @type {import('@opentelemetry/api').Span} */ span) => { + try { + return await fn(span); + } catch (error) { + if (error instanceof HttpError) { + span.setAttributes({ + [`${name}.result.type`]: 'known_error', + [`${name}.result.status`]: error.status, + [`${name}.result.message`]: error.body.message + }); + if (error.status >= 500) { + span.recordException({ + name: 'HttpError', + message: error.body.message + }); + span.setStatus({ + code: SpanStatusCode.ERROR, + message: error.body.message + }); + } + } else if (error instanceof Redirect) { + span.setAttributes({ + [`${name}.result.type`]: 'redirect', + [`${name}.result.status`]: error.status, + [`${name}.result.location`]: error.location + }); + } else if (error instanceof Error) { + span.setAttributes({ + [`${name}.result.type`]: 'unknown_error' + }); span.recordException({ - name: 'HttpError', - message: error.body.message + name: error.name, + message: error.message, + stack: error.stack }); span.setStatus({ code: SpanStatusCode.ERROR, - message: error.body.message + message: error.message }); + } else { + span.setAttributes({ + [`${name}.result.type`]: 'unknown_error' + }); + span.setStatus({ code: SpanStatusCode.ERROR }); } - } else if (error instanceof Redirect) { - span.setAttributes({ - [`${name}.result.type`]: 'redirect', - [`${name}.result.status`]: error.status, - [`${name}.result.location`]: error.location - }); - } else if (error instanceof Error) { - span.setAttributes({ - [`${name}.result.type`]: 'unknown_error' - }); - span.recordException({ - name: error.name, - message: error.message, - stack: error.stack - }); - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message - }); - } else { - span.setAttributes({ - [`${name}.result.type`]: 'unknown_error' - }); - span.setStatus({ code: SpanStatusCode.ERROR }); - } - throw error; - } finally { - span.end(); + throw error; + } finally { + span.end(); + } } - }); + ); } diff --git a/packages/kit/test/apps/basics/tsconfig.json b/packages/kit/test/apps/basics/tsconfig.json index 1d665886266b..8236dd184898 100644 --- a/packages/kit/test/apps/basics/tsconfig.json +++ b/packages/kit/test/apps/basics/tsconfig.json @@ -4,7 +4,8 @@ "checkJs": true, "esModuleInterop": true, "noEmit": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "skipLibCheck": true }, "extends": "./.svelte-kit/tsconfig.json" } diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 9705e4a0b9bd..941177ee39e7 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -3371,6 +3371,25 @@ declare module '$app/stores' { check(): Promise; }; + export {}; +} + +declare module '@opentelemetry/api' { + /* Users may not have this package installed, this prevents errors when using `tsc` without it. */ + /* eslint-disable @typescript-eslint/no-empty-object-type */ + + type _any = any; + + export interface Span extends _any {} + + export interface Tracer extends _any {} + + export interface SpanContext extends _any {} + + export interface PropagationAPI extends _any {} + + export interface ContextAPI extends _any {} + export {}; }/** * It's possible to tell SvelteKit how to type objects inside your app by declaring the `App` namespace. By default, a new project will have a file called `src/app.d.ts` containing the following: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3be6351ffb43..eae19dbb96f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -633,27 +633,6 @@ importers: specifier: 'catalog:' version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) - packages/kit/test/apps/async: - devDependencies: - '@sveltejs/kit': - specifier: workspace:^ - version: link:../../.. - '@sveltejs/vite-plugin-svelte': - specifier: 'catalog:' - version: 6.0.0-next.3(svelte@5.42.2)(vite@6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) - svelte: - specifier: 'catalog:' - version: 5.42.2 - svelte-check: - specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) - typescript: - specifier: ^5.5.4 - version: 5.8.3 - vite: - specifier: 'catalog:' - version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) - packages/kit/test/apps/basics: devDependencies: '@opentelemetry/api':