diff --git a/packages/deno/src/integrations/context.ts b/packages/deno/src/integrations/context.ts index f1c29dddda2b..e54f4ec21a87 100644 --- a/packages/deno/src/integrations/context.ts +++ b/packages/deno/src/integrations/context.ts @@ -1,4 +1,7 @@ -import type { Event, EventProcessor, Integration } from '@sentry/types'; +import { convertIntegrationFnToClass } from '@sentry/core'; +import type { Event, IntegrationFn } from '@sentry/types'; + +const INTEGRATION_NAME = 'DenoContext'; function getOSName(): string { switch (Deno.build.os) { @@ -19,7 +22,7 @@ function getOSRelease(): string | undefined { : undefined; } -async function denoRuntime(event: Event): Promise { +async function addDenoRuntimeContext(event: Event): Promise { event.contexts = { ...{ app: { @@ -49,21 +52,15 @@ async function denoRuntime(event: Event): Promise { return event; } -/** Adds Electron context to events. */ -export class DenoContext implements Integration { - /** @inheritDoc */ - public static id = 'DenoContext'; - - /** @inheritDoc */ - public name: string = DenoContext.id; - - /** @inheritDoc */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void): void { - // noop - } +const denoContextIntegration: IntegrationFn = () => { + return { + name: INTEGRATION_NAME, + processEvent(event) { + return addDenoRuntimeContext(event); + }, + }; +}; - /** @inheritDoc */ - public processEvent(event: Event): Promise { - return denoRuntime(event); - } -} +/** Adds Deno context to events. */ +// eslint-disable-next-line deprecation/deprecation +export const DenoContext = convertIntegrationFnToClass(INTEGRATION_NAME, denoContextIntegration); diff --git a/packages/deno/src/integrations/contextlines.ts b/packages/deno/src/integrations/contextlines.ts index 8c6ef510fd2e..38fe0efd3433 100644 --- a/packages/deno/src/integrations/contextlines.ts +++ b/packages/deno/src/integrations/contextlines.ts @@ -1,6 +1,8 @@ -import type { Event, EventProcessor, Integration, StackFrame } from '@sentry/types'; +import { convertIntegrationFnToClass } from '@sentry/core'; +import type { Event, IntegrationFn, StackFrame } from '@sentry/types'; import { LRUMap, addContextToFrame } from '@sentry/utils'; +const INTEGRATION_NAME = 'ContextLines'; const FILE_CONTENT_CACHE = new LRUMap(100); const DEFAULT_LINES_OF_CONTEXT = 7; @@ -45,73 +47,54 @@ interface ContextLinesOptions { frameContextLines?: number; } -/** Add node modules / packages to the event */ -export class ContextLines implements Integration { - /** - * @inheritDoc - */ - public static id = 'ContextLines'; - - /** - * @inheritDoc - */ - public name: string = ContextLines.id; - - public constructor(private readonly _options: ContextLinesOptions = {}) {} - - /** Get's the number of context lines to add */ - private get _contextLines(): number { - return this._options.frameContextLines !== undefined ? this._options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; - } - - /** - * @inheritDoc - */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void): void { - // noop - } +const denoContextLinesIntegration: IntegrationFn = (options: ContextLinesOptions = {}) => { + const contextLines = options.frameContextLines !== undefined ? options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; - /** @inheritDoc */ - public processEvent(event: Event): Promise { - return this.addSourceContext(event); - } + return { + name: INTEGRATION_NAME, + processEvent(event) { + return addSourceContext(event, contextLines); + }, + }; +}; - /** Processes an event and adds context lines */ - public async addSourceContext(event: Event): Promise { - if (this._contextLines > 0 && event.exception && event.exception.values) { - for (const exception of event.exception.values) { - if (exception.stacktrace && exception.stacktrace.frames) { - await this.addSourceContextToFrames(exception.stacktrace.frames); - } +/** Add node modules / packages to the event */ +// eslint-disable-next-line deprecation/deprecation +export const ContextLines = convertIntegrationFnToClass(INTEGRATION_NAME, denoContextLinesIntegration); + +/** Processes an event and adds context lines */ +async function addSourceContext(event: Event, contextLines: number): Promise { + if (contextLines > 0 && event.exception && event.exception.values) { + for (const exception of event.exception.values) { + if (exception.stacktrace && exception.stacktrace.frames) { + await addSourceContextToFrames(exception.stacktrace.frames, contextLines); } } - - return event; } - /** Adds context lines to frames */ - public async addSourceContextToFrames(frames: StackFrame[]): Promise { - const contextLines = this._contextLines; - - for (const frame of frames) { - // Only add context if we have a filename and it hasn't already been added - if (frame.filename && frame.in_app && frame.context_line === undefined) { - const permission = await Deno.permissions.query({ - name: 'read', - path: frame.filename, - }); - - if (permission.state == 'granted') { - const sourceFile = await readSourceFile(frame.filename); + return event; +} - if (sourceFile) { - try { - const lines = sourceFile.split('\n'); - addContextToFrame(lines, frame, contextLines); - } catch (_) { - // anomaly, being defensive in case - // unlikely to ever happen in practice but can definitely happen in theory - } +/** Adds context lines to frames */ +async function addSourceContextToFrames(frames: StackFrame[], contextLines: number): Promise { + for (const frame of frames) { + // Only add context if we have a filename and it hasn't already been added + if (frame.filename && frame.in_app && frame.context_line === undefined) { + const permission = await Deno.permissions.query({ + name: 'read', + path: frame.filename, + }); + + if (permission.state == 'granted') { + const sourceFile = await readSourceFile(frame.filename); + + if (sourceFile) { + try { + const lines = sourceFile.split('\n'); + addContextToFrame(lines, frame, contextLines); + } catch (_) { + // anomaly, being defensive in case + // unlikely to ever happen in practice but can definitely happen in theory } } } diff --git a/packages/deno/src/integrations/deno-cron.ts b/packages/deno/src/integrations/deno-cron.ts index 475d3e9131b7..2216c7ca70c4 100644 --- a/packages/deno/src/integrations/deno-cron.ts +++ b/packages/deno/src/integrations/deno-cron.ts @@ -1,6 +1,5 @@ -import { withMonitor } from '@sentry/core'; -import type { Integration } from '@sentry/types'; -import type { DenoClient } from '../client'; +import { convertIntegrationFnToClass, withMonitor } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/types'; import { parseScheduleToString } from './deno-cron-format'; type CronOptions = { backoffSchedule?: number[]; signal?: AbortSignal }; @@ -8,54 +7,50 @@ type CronFn = () => void | Promise; // Parameters doesn't work well with the overloads 🤔 type CronParams = [string, string | Deno.CronSchedule, CronFn | CronOptions, CronFn | CronOptions | undefined]; +const INTEGRATION_NAME = 'DenoCron'; + +const denoCronIntegration: IntegrationFn = () => { + return { + name: INTEGRATION_NAME, + setupOnce() { + // eslint-disable-next-line deprecation/deprecation + if (!Deno.cron) { + // The cron API is not available in this Deno version use --unstable flag! + return; + } + + // eslint-disable-next-line deprecation/deprecation + Deno.cron = new Proxy(Deno.cron, { + apply(target, thisArg, argArray: CronParams) { + const [monitorSlug, schedule, opt1, opt2] = argArray; + let options: CronOptions | undefined; + let fn: CronFn; + + if (typeof opt1 === 'function' && typeof opt2 !== 'function') { + fn = opt1; + options = opt2; + } else if (typeof opt1 !== 'function' && typeof opt2 === 'function') { + fn = opt2; + options = opt1; + } + + async function cronCalled(): Promise { + await withMonitor(monitorSlug, async () => fn(), { + schedule: { type: 'crontab', value: parseScheduleToString(schedule) }, + // (minutes) so 12 hours - just a very high arbitrary number since we don't know the actual duration of the users cron job + maxRuntime: 60 * 12, + // Deno Deploy docs say that the cron job will be called within 1 minute of the scheduled time + checkinMargin: 1, + }); + } + + return target.call(thisArg, monitorSlug, schedule, options || {}, cronCalled); + }, + }); + }, + }; +}; + /** Instruments Deno.cron to automatically capture cron check-ins */ -export class DenoCron implements Integration { - /** @inheritDoc */ - public static id = 'DenoCron'; - - /** @inheritDoc */ - public name: string = DenoCron.id; - - /** @inheritDoc */ - public setupOnce(): void { - // - } - - /** @inheritDoc */ - public setup(): void { - // eslint-disable-next-line deprecation/deprecation - if (!Deno.cron) { - // The cron API is not available in this Deno version use --unstable flag! - return; - } - - // eslint-disable-next-line deprecation/deprecation - Deno.cron = new Proxy(Deno.cron, { - apply(target, thisArg, argArray: CronParams) { - const [monitorSlug, schedule, opt1, opt2] = argArray; - let options: CronOptions | undefined; - let fn: CronFn; - - if (typeof opt1 === 'function' && typeof opt2 !== 'function') { - fn = opt1; - options = opt2; - } else if (typeof opt1 !== 'function' && typeof opt2 === 'function') { - fn = opt2; - options = opt1; - } - - async function cronCalled(): Promise { - await withMonitor(monitorSlug, async () => fn(), { - schedule: { type: 'crontab', value: parseScheduleToString(schedule) }, - // (minutes) so 12 hours - just a very high arbitrary number since we don't know the actual duration of the users cron job - maxRuntime: 60 * 12, - // Deno Deploy docs say that the cron job will be called within 1 minute of the scheduled time - checkinMargin: 1, - }); - } - - return target.call(thisArg, monitorSlug, schedule, options || {}, cronCalled); - }, - }); - } -} +// eslint-disable-next-line deprecation/deprecation +export const DenoCron = convertIntegrationFnToClass(INTEGRATION_NAME, denoCronIntegration); diff --git a/packages/deno/src/integrations/globalhandlers.ts b/packages/deno/src/integrations/globalhandlers.ts index 4160e3f4b3c6..06194037b6d1 100644 --- a/packages/deno/src/integrations/globalhandlers.ts +++ b/packages/deno/src/integrations/globalhandlers.ts @@ -1,57 +1,41 @@ import type { ServerRuntimeClient } from '@sentry/core'; +import { convertIntegrationFnToClass } from '@sentry/core'; import { captureEvent } from '@sentry/core'; import { getClient } from '@sentry/core'; import { flush } from '@sentry/core'; -import type { Client, Event, Integration, Primitive, StackParser } from '@sentry/types'; +import type { Client, Event, IntegrationFn, Primitive, StackParser } from '@sentry/types'; import { eventFromUnknownInput, isPrimitive } from '@sentry/utils'; type GlobalHandlersIntegrationsOptionKeys = 'error' | 'unhandledrejection'; -/** JSDoc */ type GlobalHandlersIntegrations = Record; +const INTEGRATION_NAME = 'GlobalHandlers'; let isExiting = false; -/** Global handlers */ -export class GlobalHandlers implements Integration { - /** - * @inheritDoc - */ - public static id = 'GlobalHandlers'; - - /** - * @inheritDoc - */ - public name: string = GlobalHandlers.id; - - /** JSDoc */ - private readonly _options: GlobalHandlersIntegrations; - - /** JSDoc */ - public constructor(options?: GlobalHandlersIntegrations) { - this._options = { - error: true, - unhandledrejection: true, - ...options, - }; - } - /** - * @inheritDoc - */ - public setupOnce(): void { - // noop - } +const globalHandlersIntegration: IntegrationFn = (options?: GlobalHandlersIntegrations) => { + const _options = { + error: true, + unhandledrejection: true, + ...options, + }; - /** @inheritdoc */ - public setup(client: Client): void { - if (this._options.error) { - installGlobalErrorHandler(client); - } - if (this._options.unhandledrejection) { - installGlobalUnhandledRejectionHandler(client); - } - } -} + return { + name: INTEGRATION_NAME, + setup(client) { + if (_options.error) { + installGlobalErrorHandler(client); + } + if (_options.unhandledrejection) { + installGlobalUnhandledRejectionHandler(client); + } + }, + }; +}; + +/** Global handlers */ +// eslint-disable-next-line deprecation/deprecation +export const GlobalHandlers = convertIntegrationFnToClass(INTEGRATION_NAME, globalHandlersIntegration); function installGlobalErrorHandler(client: Client): void { globalThis.addEventListener('error', data => { diff --git a/packages/deno/src/integrations/normalizepaths.ts b/packages/deno/src/integrations/normalizepaths.ts index ab705a3a20a0..a8143c8b078b 100644 --- a/packages/deno/src/integrations/normalizepaths.ts +++ b/packages/deno/src/integrations/normalizepaths.ts @@ -1,6 +1,9 @@ -import type { Event, EventProcessor, Integration } from '@sentry/types'; +import { convertIntegrationFnToClass } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/types'; import { createStackParser, dirname, nodeStackLineParser } from '@sentry/utils'; +const INTEGRATION_NAME = 'NormalizePaths'; + function appRootFromErrorStack(error: Error): string | undefined { // We know at the other end of the stack from here is the entry point that called 'init' // We assume that this stacktrace will traverse the root of the app @@ -52,52 +55,46 @@ function getCwd(): string | undefined { return undefined; } -// Cached here -let appRoot: string | undefined; - -function getAppRoot(error: Error): string | undefined { - if (appRoot === undefined) { - appRoot = getCwd() || appRootFromErrorStack(error); - } - - return appRoot; -} - -/** Normalises paths to the app root directory. */ -export class NormalizePaths implements Integration { - /** @inheritDoc */ - public static id = 'NormalizePaths'; +const normalizePathsIntegration: IntegrationFn = () => { + // Cached here + let appRoot: string | undefined; - /** @inheritDoc */ - public name: string = NormalizePaths.id; + function getAppRoot(error: Error): string | undefined { + if (appRoot === undefined) { + appRoot = getCwd() || appRootFromErrorStack(error); + } - /** @inheritDoc */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void): void { - // noop + return appRoot; } - /** @inheritDoc */ - public processEvent(event: Event): Event | null { - // This error.stack hopefully contains paths that traverse the app cwd - const error = new Error(); + return { + name: INTEGRATION_NAME, + processEvent(event) { + // This error.stack hopefully contains paths that traverse the app cwd + const error = new Error(); - const appRoot = getAppRoot(error); + const appRoot = getAppRoot(error); - if (appRoot) { - for (const exception of event.exception?.values || []) { - for (const frame of exception.stacktrace?.frames || []) { - if (frame.filename && frame.in_app) { - const startIndex = frame.filename.indexOf(appRoot); + if (appRoot) { + for (const exception of event.exception?.values || []) { + for (const frame of exception.stacktrace?.frames || []) { + if (frame.filename && frame.in_app) { + const startIndex = frame.filename.indexOf(appRoot); - if (startIndex > -1) { - const endIndex = startIndex + appRoot.length; - frame.filename = `app://${frame.filename.substring(endIndex)}`; + if (startIndex > -1) { + const endIndex = startIndex + appRoot.length; + frame.filename = `app://${frame.filename.substring(endIndex)}`; + } } } } } - } - return event; - } -} + return event; + }, + }; +}; + +/** Normalises paths to the app root directory. */ +// eslint-disable-next-line deprecation/deprecation +export const NormalizePaths = convertIntegrationFnToClass(INTEGRATION_NAME, normalizePathsIntegration);