From 8863965d078b4a10c942384724e3aa3d8730a386 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Mon, 18 Feb 2019 10:19:30 +0100 Subject: [PATCH] ref: Move public interfaces to types package (#1889) * fix: Events created from exception shouldnt have top-level message attribute (#1831) * Use SyncPromise internally & Remove deprecated API & Trim Public API (#1858) * ref: Move PromiseBuffer and Error to utils package * ref: Remove all async api * ref: Remove invokeClientAsync function * feat: Finish Node SDK * feat: SyncPromise implementation * fix: Browser SDK * fix: beforeSend callback * fix: Core tests * fix: linting and tests * fix: browser npm package + add error for manual tests * meta: Remove deprecations * fix: Typedocs and public exposed functions * meta: Changelog * Apply suggestions from code review Co-Authored-By: HazAT * fix: CodeReview * feat: https://github.com/getsentry/sentry-javascript/pull/1871 * ref: Move public interfaces to types package Move addBreadcrumb from the client to the hub * fix: Tests * fix: Small type issues * ref: Remove scope listeners * fix: Add flush to interface * ref: Remove interfaces from core --- CHANGELOG.md | 2 +- packages/browser/rollup.config.js | 2 +- packages/browser/src/backend.ts | 18 +- packages/browser/src/client.ts | 4 +- packages/browser/src/index.ts | 6 +- .../browser/src/integrations/breadcrumbs.ts | 6 +- .../src/integrations/globalhandlers.ts | 10 +- packages/browser/src/integrations/helpers.ts | 8 +- .../browser/src/integrations/linkederrors.ts | 8 +- .../src/integrations/pluggable/ember.ts | 4 +- .../browser/src/integrations/pluggable/vue.ts | 4 +- packages/browser/src/integrations/trycatch.ts | 6 +- .../browser/src/integrations/useragent.ts | 4 +- packages/browser/src/parsers.ts | 14 +- packages/browser/src/transports/base.ts | 6 +- packages/browser/src/transports/beacon.ts | 4 +- packages/browser/src/transports/fetch.ts | 4 +- packages/browser/src/transports/xhr.ts | 6 +- packages/browser/test/index.test.ts | 8 +- .../browser/test/integrations/helpers.test.ts | 20 +- .../browser/test/mocks/simpletransport.ts | 4 +- packages/core/src/basebackend.ts | 69 ++-- packages/core/src/baseclient.ts | 82 +---- packages/core/src/index.ts | 1 - packages/core/src/integration.ts | 3 +- packages/core/src/integrations/dedupe.ts | 20 +- .../core/src/integrations/extraerrordata.ts | 8 +- .../core/src/integrations/functiontostring.ts | 4 +- .../core/src/integrations/inboundfilters.ts | 19 +- .../core/src/integrations/pluggable/debug.ts | 4 +- .../integrations/pluggable/rewriteframes.ts | 6 +- packages/core/src/interfaces.ts | 281 -------------- packages/core/src/sdk.ts | 2 +- packages/core/src/transports/noop.ts | 4 +- packages/core/test/lib/base.test.ts | 55 +-- packages/core/test/mocks/backend.ts | 20 +- packages/core/test/mocks/integration.ts | 4 +- packages/hub/src/hub.ts | 76 ++-- packages/hub/src/interfaces.ts | 3 +- packages/hub/src/scope.ts | 91 ++--- packages/hub/test/hub.test.ts | 109 +----- packages/hub/test/scope.test.ts | 57 ++- packages/minimal/src/index.ts | 4 +- packages/minimal/test/lib/minimal.test.ts | 51 +-- packages/minimal/test/mocks/client.ts | 2 +- packages/minimal/test/tslint.json | 3 +- packages/node/src/backend.ts | 18 +- packages/node/src/client.ts | 4 +- packages/node/src/handlers.ts | 8 +- packages/node/src/index.ts | 6 +- .../node/src/integrations/linkederrors.ts | 22 +- .../src/integrations/pluggable/transaction.ts | 6 +- packages/node/src/parsers.ts | 12 +- packages/node/src/transports/base.ts | 10 +- packages/node/src/transports/http.ts | 4 +- packages/node/src/transports/https.ts | 4 +- .../node/test/helper/settimeouttransport.ts | 6 +- packages/node/test/index.test.ts | 23 +- .../test/integrations/linkederrors.test.ts | 6 +- packages/node/test/parsers.test.ts | 1 - packages/types/src/breadcrumb.ts | 17 + packages/types/src/client.ts | 73 ++++ packages/types/src/dsn.ts | 37 ++ packages/types/src/event.ts | 45 +++ packages/types/src/eventprocessor.ts | 9 + packages/types/src/exception.ts | 10 + packages/types/src/index.ts | 347 ++---------------- packages/types/src/integration.ts | 19 + packages/types/src/loglevel.ts | 11 + packages/types/src/mechanism.ts | 8 + packages/types/src/options.ts | 107 ++++++ packages/types/src/package.ts | 5 + packages/types/src/request.ts | 10 + packages/types/src/response.ts | 9 + packages/types/src/scope.ts | 53 +++ packages/types/src/sdkinfo.ts | 9 + packages/types/src/severity.ts | 47 +++ packages/types/src/stackframe.ts | 15 + packages/types/src/stacktrace.ts | 7 + packages/types/src/status.ts | 44 +++ packages/types/src/thread.ts | 10 + packages/types/src/transport.ts | 39 ++ packages/types/src/user.ts | 8 + packages/types/src/wrappedfunction.ts | 7 + packages/utils/src/misc.ts | 10 +- packages/utils/src/object.ts | 6 +- 86 files changed, 1038 insertions(+), 1180 deletions(-) delete mode 100644 packages/core/src/interfaces.ts create mode 100644 packages/types/src/breadcrumb.ts create mode 100644 packages/types/src/client.ts create mode 100644 packages/types/src/dsn.ts create mode 100644 packages/types/src/event.ts create mode 100644 packages/types/src/eventprocessor.ts create mode 100644 packages/types/src/exception.ts create mode 100644 packages/types/src/integration.ts create mode 100644 packages/types/src/loglevel.ts create mode 100644 packages/types/src/mechanism.ts create mode 100644 packages/types/src/options.ts create mode 100644 packages/types/src/package.ts create mode 100644 packages/types/src/request.ts create mode 100644 packages/types/src/response.ts create mode 100644 packages/types/src/scope.ts create mode 100644 packages/types/src/sdkinfo.ts create mode 100644 packages/types/src/severity.ts create mode 100644 packages/types/src/stackframe.ts create mode 100644 packages/types/src/stacktrace.ts create mode 100644 packages/types/src/status.ts create mode 100644 packages/types/src/thread.ts create mode 100644 packages/types/src/transport.ts create mode 100644 packages/types/src/user.ts create mode 100644 packages/types/src/wrappedfunction.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 528db185a9e6..5d2f975d8052 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ since we removed some methods from the public API and removed some classes from - **breaking** [node] fix: Events created from exception shouldn't have top-level message attribute - [utils] ref: Update wrap method to hide internal sentry flags -- [utils] fix: Make internal Sentry flags non-enumerable in fill util +- [utils] fix: Make internal Sentry flags non-enumerable in fill utils - [utils] ref: Move `SentryError` + `PromiseBuffer` to utils - **breaking** [core] ref: Use `SyncPromise` internally, this reduces memory pressure by a lot. - **breaking** [browser] ref: Removed `BrowserBackend` from default export. diff --git a/packages/browser/rollup.config.js b/packages/browser/rollup.config.js index 41227d9efdae..57d039b440dd 100644 --- a/packages/browser/rollup.config.js +++ b/packages/browser/rollup.config.js @@ -66,7 +66,7 @@ export default [ interop: false, sourcemap: true, }, - external: ['@sentry/core', '@sentry/hub', '@sentry/minimal'], + external: ['@sentry/core', '@sentry/hub', '@sentry/minimal', 'tslib'], plugins: [ typescript({ tsconfig: 'tsconfig.build.json', diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index cca0c16014e3..73cbb77282ef 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -1,5 +1,5 @@ -import { BaseBackend, Options } from '@sentry/core'; -import { SentryEvent, SentryEventHint, Severity, Transport } from '@sentry/types'; +import { BaseBackend } from '@sentry/core'; +import { Event, EventHint, Options, Severity, Transport } from '@sentry/types'; import { isDOMError, isDOMException, isError, isErrorEvent, isPlainObject } from '@sentry/utils/is'; import { supportsBeacon, supportsFetch } from '@sentry/utils/supports'; import { SyncPromise } from '@sentry/utils/syncpromise'; @@ -56,8 +56,8 @@ export class BrowserBackend extends BaseBackend { /** * @inheritDoc */ - public eventFromException(exception: any, hint?: SentryEventHint): SyncPromise { - let event: SentryEvent; + public eventFromException(exception: any, hint?: EventHint): SyncPromise { + let event: Event; if (isErrorEvent(exception as ErrorEvent) && (exception as ErrorEvent).error) { // If it is an ErrorEvent with `error` property, extract it to get actual Error @@ -108,7 +108,7 @@ export class BrowserBackend extends BaseBackend { /** * This is an internal helper function that creates an event. */ - private buildEvent(event: SentryEvent, hint?: SentryEventHint): SentryEvent { + private buildEvent(event: Event, hint?: EventHint): Event { return { ...event, event_id: hint && hint.event_id, @@ -125,12 +125,8 @@ export class BrowserBackend extends BaseBackend { /** * @inheritDoc */ - public eventFromMessage( - message: string, - level: Severity = Severity.Info, - hint?: SentryEventHint, - ): SyncPromise { - const event: SentryEvent = { + public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): SyncPromise { + const event: Event = { event_id: hint && hint.event_id, level, message, diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 9ad73c8d5ce0..03aab99fc2d0 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ import { API, BaseClient, Scope } from '@sentry/core'; -import { DsnLike, SentryEvent, SentryEventHint } from '@sentry/types'; +import { DsnLike, Event, EventHint } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import { getGlobalObject } from '@sentry/utils/misc'; import { SyncPromise } from '@sentry/utils/syncpromise'; @@ -50,7 +50,7 @@ export class BrowserClient extends BaseClient { /** * @inheritDoc */ - protected prepareEvent(event: SentryEvent, scope?: Scope, hint?: SentryEventHint): SyncPromise { + protected prepareEvent(event: Event, scope?: Scope, hint?: EventHint): SyncPromise { event.platform = event.platform || 'javascript'; event.sdk = { ...event.sdk, diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 7ace8a2bc6c6..84a4613191c5 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -2,9 +2,9 @@ export { Breadcrumb, Request, SdkInfo, - SentryEvent, - SentryException, - SentryResponse, + Event, + Exception, + Response, Severity, StackFrame, Stacktrace, diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index e6e1c56ed7cc..1a9bfc2613c1 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,5 +1,5 @@ import { API, getCurrentHub } from '@sentry/core'; -import { Breadcrumb, Integration, SentryBreadcrumbHint, Severity } from '@sentry/types'; +import { Breadcrumb, BreadcrumbHint, Integration, Severity } from '@sentry/types'; import { isFunction, isString } from '@sentry/utils/is'; import { logger } from '@sentry/utils/logger'; import { getEventDescription, getGlobalObject, parseUrl } from '@sentry/utils/misc'; @@ -454,9 +454,9 @@ export class Breadcrumbs implements Integration { /** * Helper that checks if integration is enabled on the client. * @param breadcrumb Breadcrumb - * @param hint SentryBreadcrumbHint + * @param hint BreadcrumbHint */ - public static addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint): void { + public static addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { if (getCurrentHub().getIntegration(Breadcrumbs)) { getCurrentHub().addBreadcrumb(breadcrumb, hint); } diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 85441d71fdcc..1b04a2cfdb28 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/core'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; import { safeNormalize, serialize } from '@sentry/utils/object'; import { truncate } from '@sentry/utils/string'; @@ -83,11 +83,11 @@ export class GlobalHandlers implements Integration { } /** - * This function creates an SentryEvent from an TraceKitStackTrace. + * This function creates an Event from an TraceKitStackTrace. * - * @param stacktrace TraceKitStackTrace to be converted to an SentryEvent. + * @param stacktrace TraceKitStackTrace to be converted to an Event. */ - private eventFromGlobalHandler(stacktrace: TraceKitStackTrace): SentryEvent { + private eventFromGlobalHandler(stacktrace: TraceKitStackTrace): Event { const event = eventFromStacktrace(stacktrace); const data: { [key: string]: string } = { @@ -102,7 +102,7 @@ export class GlobalHandlers implements Integration { data.name = stacktrace.name; } - const newEvent: SentryEvent = { + const newEvent: Event = { ...event, exception: { ...event.exception, diff --git a/packages/browser/src/integrations/helpers.ts b/packages/browser/src/integrations/helpers.ts index 913fc6100990..bddf5ff0324f 100644 --- a/packages/browser/src/integrations/helpers.ts +++ b/packages/browser/src/integrations/helpers.ts @@ -1,5 +1,5 @@ import { captureException, getCurrentHub, withScope } from '@sentry/core'; -import { Mechanism, SentryEvent, SentryWrappedFunction } from '@sentry/types'; +import { Event as SentryEvent, Mechanism, WrappedFunction } from '@sentry/types'; import { isFunction } from '@sentry/utils/is'; import { htmlTreeAsString } from '@sentry/utils/misc'; import { serializeObject } from '@sentry/utils/object'; @@ -36,11 +36,11 @@ export function ignoreNextOnError(): void { * @hidden */ export function wrap( - fn: SentryWrappedFunction, + fn: WrappedFunction, options: { mechanism?: Mechanism; } = {}, - before?: SentryWrappedFunction, + before?: WrappedFunction, ): any { if (!isFunction(fn)) { return fn; @@ -63,7 +63,7 @@ export function wrap( return fn; } - const sentryWrapped: SentryWrappedFunction = function(this: any): void { + const sentryWrapped: WrappedFunction = function(this: any): void { if (before && isFunction(before)) { before.apply(this, arguments); } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index 9433760c9928..bda28e490a6c 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Integration, SentryEvent, SentryEventHint, SentryException } from '@sentry/types'; +import { Event, EventHint, Exception, Integration } from '@sentry/types'; import { exceptionFromStacktrace } from '../parsers'; import { computeStackTrace } from '../tracekit'; @@ -47,7 +47,7 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent, hint?: SentryEventHint) => { + addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(LinkedErrors); if (self) { return self.handler(event, hint); @@ -59,7 +59,7 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public handler(event: SentryEvent, hint?: SentryEventHint): SentryEvent | null { + public handler(event: Event, hint?: EventHint): Event | null { if (!event.exception || !event.exception.values || !hint || !(hint.originalException instanceof Error)) { return event; } @@ -71,7 +71,7 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public walkErrorTree(error: ExtendedError, key: string, stack: SentryException[] = []): SentryException[] { + public walkErrorTree(error: ExtendedError, key: string, stack: Exception[] = []): Exception[] { if (!(error[key] instanceof Error) || stack.length + 1 >= this.limit) { return stack; } diff --git a/packages/browser/src/integrations/pluggable/ember.ts b/packages/browser/src/integrations/pluggable/ember.ts index d2ed2fdae4b7..970350efd53d 100644 --- a/packages/browser/src/integrations/pluggable/ember.ts +++ b/packages/browser/src/integrations/pluggable/ember.ts @@ -1,5 +1,5 @@ import { captureException, captureMessage, getCurrentHub, Scope, withScope } from '@sentry/core'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; import { getGlobalObject } from '@sentry/utils/misc'; @@ -81,7 +81,7 @@ export class Ember implements Integration { * @param scope The scope currently used. */ private addIntegrationToSdkInfo(scope: Scope): void { - scope.addEventProcessor((event: SentryEvent) => { + scope.addEventProcessor((event: Event) => { if (event.sdk) { const integrations = event.sdk.integrations || []; event.sdk = { diff --git a/packages/browser/src/integrations/pluggable/vue.ts b/packages/browser/src/integrations/pluggable/vue.ts index b91a19a9cea7..461a7844240e 100644 --- a/packages/browser/src/integrations/pluggable/vue.ts +++ b/packages/browser/src/integrations/pluggable/vue.ts @@ -1,5 +1,5 @@ import { captureException, getCurrentHub, withScope } from '@sentry/core'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; import { isPlainObject, isUndefined } from '@sentry/utils/is'; import { logger } from '@sentry/utils/logger'; import { getGlobalObject } from '@sentry/utils/misc'; @@ -92,7 +92,7 @@ export class Vue implements Integration { scope.setExtra(key, metadata[key]); }); - scope.addEventProcessor((event: SentryEvent) => { + scope.addEventProcessor((event: Event) => { if (event.sdk) { const integrations = event.sdk.integrations || []; event.sdk = { diff --git a/packages/browser/src/integrations/trycatch.ts b/packages/browser/src/integrations/trycatch.ts index 2c9e56aa7479..7ff4c916557a 100644 --- a/packages/browser/src/integrations/trycatch.ts +++ b/packages/browser/src/integrations/trycatch.ts @@ -1,4 +1,4 @@ -import { Integration, SentryWrappedFunction } from '@sentry/types'; +import { Integration, WrappedFunction } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; import { fill } from '@sentry/utils/object'; import { breadcrumbEventHandler, keypressEventHandler, wrap } from './helpers'; @@ -124,7 +124,7 @@ export class TryCatch implements Integration { this, eventName, wrap( - (fn as any) as SentryWrappedFunction, + (fn as any) as WrappedFunction, { mechanism: { data: { @@ -152,7 +152,7 @@ export class TryCatch implements Integration { fn: EventListenerObject, options?: boolean | EventListenerOptions, ): () => void { - let callback = (fn as any) as SentryWrappedFunction; + let callback = (fn as any) as WrappedFunction; try { callback = callback && (callback.__sentry_wrapped__ || callback); } catch (e) { diff --git a/packages/browser/src/integrations/useragent.ts b/packages/browser/src/integrations/useragent.ts index 8f8eea74ea53..0cd51c512e33 100644 --- a/packages/browser/src/integrations/useragent.ts +++ b/packages/browser/src/integrations/useragent.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; const global = getGlobalObject() as Window; @@ -20,7 +20,7 @@ export class UserAgent implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent) => { + addGlobalEventProcessor((event: Event) => { if (getCurrentHub().getIntegration(UserAgent)) { if (!global.navigator || !global.location) { return event; diff --git a/packages/browser/src/parsers.ts b/packages/browser/src/parsers.ts index 3be0460277a0..67a03f5218f4 100644 --- a/packages/browser/src/parsers.ts +++ b/packages/browser/src/parsers.ts @@ -1,4 +1,4 @@ -import { SentryEvent, SentryException, StackFrame } from '@sentry/types'; +import { Event, Exception, StackFrame } from '@sentry/types'; import { limitObjectDepthToSize, serializeKeysToEventMessage } from '@sentry/utils/object'; import { includes } from '@sentry/utils/string'; import { md5 } from './md5'; @@ -11,10 +11,10 @@ const STACKTRACE_LIMIT = 50; * @param stacktrace TraceKitStackTrace that will be converted to an exception * @hidden */ -export function exceptionFromStacktrace(stacktrace: TraceKitStackTrace): SentryException { +export function exceptionFromStacktrace(stacktrace: TraceKitStackTrace): Exception { const frames = prepareFramesForEvent(stacktrace.stack); - const exception: SentryException = { + const exception: Exception = { type: stacktrace.name, value: stacktrace.message, }; @@ -34,9 +34,9 @@ export function exceptionFromStacktrace(stacktrace: TraceKitStackTrace): SentryE /** * @hidden */ -export function eventFromPlainObject(exception: {}, syntheticException: Error | null): SentryEvent { +export function eventFromPlainObject(exception: {}, syntheticException: Error | null): Event { const exceptionKeys = Object.keys(exception).sort(); - const event: SentryEvent = { + const event: Event = { extra: { __serialized__: limitObjectDepthToSize(exception), }, @@ -58,7 +58,7 @@ export function eventFromPlainObject(exception: {}, syntheticException: Error | /** * @hidden */ -export function eventFromStacktrace(stacktrace: TraceKitStackTrace): SentryEvent { +export function eventFromStacktrace(stacktrace: TraceKitStackTrace): Event { const exception = exceptionFromStacktrace(stacktrace); return { @@ -113,7 +113,7 @@ export function prepareFramesForEvent(stack: TraceKitStackFrame[]): StackFrame[] * @param type Type of the exception. * @hidden */ -export function addExceptionTypeValue(event: SentryEvent, value?: string, type?: string): void { +export function addExceptionTypeValue(event: Event, value?: string, type?: string): void { event.exception = event.exception || {}; event.exception.values = event.exception.values || []; event.exception.values[0] = event.exception.values[0] || {}; diff --git a/packages/browser/src/transports/base.ts b/packages/browser/src/transports/base.ts index 17d9f5bd2b1a..baf9688fe9be 100644 --- a/packages/browser/src/transports/base.ts +++ b/packages/browser/src/transports/base.ts @@ -1,5 +1,5 @@ import { API } from '@sentry/core'; -import { SentryResponse, Transport, TransportOptions } from '@sentry/types'; +import { Response, Transport, TransportOptions } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import { PromiseBuffer } from '@sentry/utils/promisebuffer'; @@ -11,7 +11,7 @@ export abstract class BaseTransport implements Transport { public url: string; /** A simple buffer holding all requests. */ - protected readonly buffer: PromiseBuffer = new PromiseBuffer(30); + protected readonly buffer: PromiseBuffer = new PromiseBuffer(30); public constructor(public options: TransportOptions) { this.url = new API(this.options.dsn).getStoreEndpointWithUrlEncodedAuth(); @@ -20,7 +20,7 @@ export abstract class BaseTransport implements Transport { /** * @inheritDoc */ - public async sendEvent(_: string): Promise { + public async sendEvent(_: string): Promise { throw new SentryError('Transport Class has to implement `sendEvent` method'); } diff --git a/packages/browser/src/transports/beacon.ts b/packages/browser/src/transports/beacon.ts index 4bd547d7a683..e2ffd37ac67f 100644 --- a/packages/browser/src/transports/beacon.ts +++ b/packages/browser/src/transports/beacon.ts @@ -1,4 +1,4 @@ -import { SentryResponse, Status } from '@sentry/types'; +import { Response, Status } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; import { BaseTransport } from './base'; @@ -9,7 +9,7 @@ export class BeaconTransport extends BaseTransport { /** * @inheritDoc */ - public async sendEvent(body: string): Promise { + public async sendEvent(body: string): Promise { const result = global.navigator.sendBeacon(this.url, body); return this.buffer.add( diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index c07087c1d794..f27356b41bd7 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -1,4 +1,4 @@ -import { SentryResponse, Status } from '@sentry/types'; +import { Response, Status } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; import { supportsReferrerPolicy } from '@sentry/utils/supports'; import { BaseTransport } from './base'; @@ -10,7 +10,7 @@ export class FetchTransport extends BaseTransport { /** * @inheritDoc */ - public async sendEvent(body: string): Promise { + public async sendEvent(body: string): Promise { const defaultOptions: RequestInit = { body, method: 'POST', diff --git a/packages/browser/src/transports/xhr.ts b/packages/browser/src/transports/xhr.ts index a6e5af9a48c9..15e6e6b9d670 100644 --- a/packages/browser/src/transports/xhr.ts +++ b/packages/browser/src/transports/xhr.ts @@ -1,4 +1,4 @@ -import { SentryResponse, Status } from '@sentry/types'; +import { Response, Status } from '@sentry/types'; import { BaseTransport } from './base'; /** `XHR` based transport */ @@ -6,9 +6,9 @@ export class XHRTransport extends BaseTransport { /** * @inheritDoc */ - public async sendEvent(body: string): Promise { + public async sendEvent(body: string): Promise { return this.buffer.add( - new Promise((resolve, reject) => { + new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.onreadystatechange = () => { diff --git a/packages/browser/test/index.test.ts b/packages/browser/test/index.test.ts index 096083173227..4df6ed90b7b4 100644 --- a/packages/browser/test/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -7,11 +7,11 @@ import { captureException, captureMessage, configureScope, + Event, getCurrentHub, init, Integrations, Scope, - SentryEvent, } from '../src'; import { SimpleTransport } from './mocks/simpletransport'; @@ -20,7 +20,7 @@ const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; declare var global: any; describe('SentryBrowser', () => { - const beforeSend: SinonSpy = spy((event: SentryEvent) => event); + const beforeSend: SinonSpy = spy((event: Event) => event); before(() => { init({ @@ -100,7 +100,7 @@ describe('SentryBrowser', () => { it('should capture a message', done => { getCurrentHub().bindClient( new BrowserClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; done(); @@ -115,7 +115,7 @@ describe('SentryBrowser', () => { it('should capture an event', done => { getCurrentHub().bindClient( new BrowserClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.message).to.equal('event'); expect(event.exception).to.be.undefined; done(); diff --git a/packages/browser/test/integrations/helpers.test.ts b/packages/browser/test/integrations/helpers.test.ts index b608d03440cb..3bd1250507fe 100644 --- a/packages/browser/test/integrations/helpers.test.ts +++ b/packages/browser/test/integrations/helpers.test.ts @@ -1,4 +1,4 @@ -import { SentryWrappedFunction } from '@sentry/types'; +import { WrappedFunction } from '@sentry/types'; import { expect } from 'chai'; import { SinonSpy, spy } from 'sinon'; import { wrap } from '../../src/integrations/helpers'; @@ -23,7 +23,7 @@ describe('wrap()', () => { }); it('bail out with the original if accessing custom props go bad', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; fn.__sentry__ = false; Object.defineProperty(fn, '__sentry_wrapped__', { get(): void { @@ -44,14 +44,14 @@ describe('wrap()', () => { }); it('returns wrapped function if original was already wrapped', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const wrapped = wrap(fn); expect(wrap(fn)).equal(wrapped); }); it('returns same wrapped function if trying to wrap it again', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const wrapped = wrap(fn); @@ -59,7 +59,7 @@ describe('wrap()', () => { }); it('calls "before" function when invoking wrapped function', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const before = spy(); const wrapped = wrap(fn, {}, before); @@ -69,7 +69,7 @@ describe('wrap()', () => { }); it('attaches metadata to original and wrapped functions', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const wrapped = wrap(fn); @@ -84,7 +84,7 @@ describe('wrap()', () => { }); it('copies over original functions properties', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; fn.some = 1337; fn.property = 'Rick'; @@ -97,7 +97,7 @@ describe('wrap()', () => { }); it('doesnt break when accessing original functions properties blows up', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; Object.defineProperty(fn, 'some', { get(): void { throw new Error('boom'); @@ -110,7 +110,7 @@ describe('wrap()', () => { }); it('recrusively wraps arguments that are functions', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const fnArgA = () => 1337; const fnArgB = () => 1337; @@ -181,7 +181,7 @@ describe('wrap()', () => { }); it('internal flags shouldnt be enumerable', () => { - const fn = (() => 1337) as SentryWrappedFunction; + const fn = (() => 1337) as WrappedFunction; const wrapped = wrap(fn); // Shouldn't show up in iteration diff --git a/packages/browser/test/mocks/simpletransport.ts b/packages/browser/test/mocks/simpletransport.ts index 2e57687e37be..23c90efb27fe 100644 --- a/packages/browser/test/mocks/simpletransport.ts +++ b/packages/browser/test/mocks/simpletransport.ts @@ -1,8 +1,8 @@ -import { SentryResponse, Status } from '../../src'; +import { Response, Status } from '../../src'; import { BaseTransport } from '../../src/transports'; export class SimpleTransport extends BaseTransport { - public async sendEvent(_: string): Promise { + public async sendEvent(_: string): Promise { return this.buffer.add( Promise.resolve({ status: Status.fromHttpCode(200), diff --git a/packages/core/src/basebackend.ts b/packages/core/src/basebackend.ts index 1067c1641073..cc0d7892262a 100644 --- a/packages/core/src/basebackend.ts +++ b/packages/core/src/basebackend.ts @@ -1,19 +1,60 @@ -import { Scope } from '@sentry/hub'; -import { Breadcrumb, SentryEvent, SentryEventHint, Severity, Transport } from '@sentry/types'; +import { Event, EventHint, Options, Severity, Transport } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import { logger } from '@sentry/utils/logger'; import { serialize } from '@sentry/utils/object'; import { SyncPromise } from '@sentry/utils/syncpromise'; -import { Backend, Options } from './interfaces'; import { NoopTransport } from './transports/noop'; -/** A class object that can instanciate Backend objects. */ +/** + * Internal platform-dependent Sentry SDK Backend. + * + * While {@link Client} contains business logic specific to an SDK, the + * Backend offers platform specific implementations for low-level operations. + * These are persisting and loading information, sending events, and hooking + * into the environment. + * + * Backends receive a handle to the Client in their constructor. When a + * Backend automatically generates events, it must pass them to + * the Client for validation and processing first. + * + * Usually, the Client will be of corresponding type, e.g. NodeBackend + * receives NodeClient. However, higher-level SDKs can choose to instanciate + * multiple Backends and delegate tasks between them. In this case, an event + * generated by one backend might very well be sent by another one. + * + * The client also provides access to options via {@link Client.getOptions}. + * @hidden + */ +export interface Backend { + /** Creates a {@link Event} from an exception. */ + eventFromException(exception: any, hint?: EventHint): SyncPromise; + + /** Creates a {@link Event} from a plain message. */ + eventFromMessage(message: string, level?: Severity, hint?: EventHint): SyncPromise; + + /** Submits the event to Sentry */ + sendEvent(event: Event): void; + + /** + * Returns the transport that is used by the backend. + * Please note that the transport gets lazy initialized so it will only be there once the first event has been sent. + * + * @returns The transport. + */ + getTransport(): Transport; +} + +/** + * A class object that can instanciate Backend objects. + * @hidden + */ export interface BackendClass { new (options: O): B; } /** * This is the base implemention of a Backend. + * @hidden */ export abstract class BaseBackend implements Backend { /** Options passed to the SDK. */ @@ -41,40 +82,26 @@ export abstract class BaseBackend implements Backend { /** * @inheritDoc */ - public eventFromException(_exception: any, _hint?: SentryEventHint): SyncPromise { + public eventFromException(_exception: any, _hint?: EventHint): SyncPromise { throw new SentryError('Backend has to implement `eventFromException` method'); } /** * @inheritDoc */ - public eventFromMessage(_message: string, _level?: Severity, _hint?: SentryEventHint): SyncPromise { + public eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): SyncPromise { throw new SentryError('Backend has to implement `eventFromMessage` method'); } /** * @inheritDoc */ - public sendEvent(event: SentryEvent): void { + public sendEvent(event: Event): void { this.transport.sendEvent(serialize(event)).catch(reason => { logger.error(`Error while sending event: ${reason}`); }); } - /** - * @inheritDoc - */ - public storeBreadcrumb(_: Breadcrumb): boolean { - return true; - } - - /** - * @inheritDoc - */ - public storeScope(_: Scope): void { - // Noop - } - /** * @inheritDoc */ diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 78374c17346a..e7d0deb27eee 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,34 +1,13 @@ import { Scope } from '@sentry/hub'; -import { - Breadcrumb, - Integration, - IntegrationClass, - SentryBreadcrumbHint, - SentryEvent, - SentryEventHint, - Severity, -} from '@sentry/types'; +import { Client, Event, EventHint, Integration, IntegrationClass, Options, Severity } from '@sentry/types'; import { isPrimitive, isThenable } from '@sentry/utils/is'; import { logger } from '@sentry/utils/logger'; -import { consoleSandbox, uuid4 } from '@sentry/utils/misc'; +import { uuid4 } from '@sentry/utils/misc'; import { truncate } from '@sentry/utils/string'; import { SyncPromise } from '@sentry/utils/syncpromise'; -import { BackendClass } from './basebackend'; +import { Backend, BackendClass } from './basebackend'; import { Dsn } from './dsn'; import { IntegrationIndex, setupIntegrations } from './integration'; -import { Backend, Client, Options } from './interfaces'; - -/** - * Default maximum number of breadcrumbs added to an event. Can be overwritten - * with {@link Options.maxBreadcrumbs}. - */ -const DEFAULT_BREADCRUMBS = 30; - -/** - * Absolute maximum number of breadcrumbs added to an event. The - * `maxBreadcrumbs` option cannot be higher than this value. - */ -const MAX_BREADCRUMBS = 100; /** * By default, truncates URL values to 250 chars @@ -107,7 +86,7 @@ export abstract class BaseClient implement /** * @inheritDoc */ - public captureException(exception: any, hint?: SentryEventHint, scope?: Scope): string | undefined { + public captureException(exception: any, hint?: EventHint, scope?: Scope): string | undefined { let eventId: string | undefined = hint && hint.event_id; this.getBackend() @@ -126,7 +105,7 @@ export abstract class BaseClient implement /** * @inheritDoc */ - public captureMessage(message: string, level?: Severity, hint?: SentryEventHint, scope?: Scope): string | undefined { + public captureMessage(message: string, level?: Severity, hint?: EventHint, scope?: Scope): string | undefined { let eventId: string | undefined = hint && hint.event_id; const promisedEvent = isPrimitive(message) @@ -148,7 +127,7 @@ export abstract class BaseClient implement /** * @inheritDoc */ - public captureEvent(event: SentryEvent, hint?: SentryEventHint, scope?: Scope): string | undefined { + public captureEvent(event: Event, hint?: EventHint, scope?: Scope): string | undefined { let eventId: string | undefined = hint && hint.event_id; this.processEvent(event, hint, scope) .then(finalEvent => { @@ -160,31 +139,6 @@ export abstract class BaseClient implement return eventId; } - /** - * @inheritDoc - */ - public addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint, scope?: Scope): void { - const { beforeBreadcrumb, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = this.getOptions(); - - if (maxBreadcrumbs <= 0) { - return; - } - - const timestamp = new Date().getTime() / 1000; - const mergedBreadcrumb = { timestamp, ...breadcrumb }; - const finalBreadcrumb = beforeBreadcrumb - ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null) - : mergedBreadcrumb; - - if (finalBreadcrumb === null) { - return; - } - - if (this.getBackend().storeBreadcrumb(finalBreadcrumb) && scope) { - scope.addBreadcrumb(finalBreadcrumb, Math.min(maxBreadcrumbs, MAX_BREADCRUMBS)); - } - } - /** * @inheritDoc */ @@ -223,10 +177,10 @@ export abstract class BaseClient implement * @param scope A scope containing event metadata. * @returns A new event with more information. */ - protected prepareEvent(event: SentryEvent, scope?: Scope, hint?: SentryEventHint): SyncPromise { - const { environment, maxBreadcrumbs = DEFAULT_BREADCRUMBS, release, dist } = this.getOptions(); + protected prepareEvent(event: Event, scope?: Scope, hint?: EventHint): SyncPromise { + const { environment, release, dist } = this.getOptions(); - const prepared = { ...event }; + const prepared: Event = { ...event }; if (prepared.environment === undefined && environment !== undefined) { prepared.environment = environment; } @@ -256,14 +210,14 @@ export abstract class BaseClient implement prepared.event_id = uuid4(); } - // We prepare the result here with a resolved SentryEvent. - let result = SyncPromise.resolve(prepared); + // We prepare the result here with a resolved Event. + let result = SyncPromise.resolve(prepared); // This should be the last thing called, since we want that // {@link Hub.addEventProcessor} gets the finished prepared event. if (scope) { // In case we have a hub we reassign it. - result = scope.applyToEvent(prepared, hint, Math.min(maxBreadcrumbs, MAX_BREADCRUMBS)); + result = scope.applyToEvent(prepared, hint); } return result; @@ -282,7 +236,7 @@ export abstract class BaseClient implement * @param scope A scope containing event metadata. * @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send. */ - protected processEvent(event: SentryEvent, hint?: SentryEventHint, scope?: Scope): SyncPromise { + protected processEvent(event: Event, hint?: EventHint, scope?: Scope): SyncPromise { const { beforeSend, sampleRate } = this.getOptions(); if (!this.isEnabled()) { @@ -302,7 +256,7 @@ export abstract class BaseClient implement return; } - let finalEvent: SentryEvent | null = prepared; + let finalEvent: Event | null = prepared; try { const isInternalException = hint && hint.data && (hint.data as { [key: string]: any }).__sentry__ === true; @@ -316,9 +270,9 @@ export abstract class BaseClient implement if ((typeof beforeSendResult as any) === 'undefined') { logger.error('`beforeSend` method has to return `null` or a valid event.'); } else if (isThenable(beforeSendResult)) { - this.handleAsyncBeforeSend(beforeSendResult as Promise, resolve, reject); + this.handleAsyncBeforeSend(beforeSendResult as Promise, resolve, reject); } else { - finalEvent = beforeSendResult as SentryEvent | null; + finalEvent = beforeSendResult as Event | null; if (finalEvent === null) { logger.log('`beforeSend` returned `null`, will not send event.'); @@ -347,8 +301,8 @@ export abstract class BaseClient implement * Resolves before send Promise and calls resolve/reject on parent SyncPromise. */ private handleAsyncBeforeSend( - beforeSend: Promise, - resolve: (event: SentryEvent) => void, + beforeSend: Promise, + resolve: (event: Event) => void, reject: (reason: string) => void, ): void { beforeSend diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 020e0c5d115a..2d8e542fd022 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -11,7 +11,6 @@ export { API } from './api'; export { BaseClient } from './baseclient'; export { BackendClass, BaseBackend } from './basebackend'; export { Dsn } from './dsn'; -export { Backend, Client, LogLevel, Options } from './interfaces'; export { initAndBind, ClientClass } from './sdk'; export { NoopTransport } from './transports/noop'; diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index 8f38d425c52a..2fc2682e1c81 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -1,6 +1,5 @@ -import { Integration } from '@sentry/types'; +import { Integration, Options } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; -import { Options } from './interfaces'; export const installedIntegrations: string[] = []; diff --git a/packages/core/src/integrations/dedupe.ts b/packages/core/src/integrations/dedupe.ts index 34b492c184dc..478d587640fc 100644 --- a/packages/core/src/integrations/dedupe.ts +++ b/packages/core/src/integrations/dedupe.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, SentryEvent, SentryException, StackFrame } from '@sentry/types'; +import { Event, Exception, Integration, StackFrame } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; import { getEventDescription } from '@sentry/utils/misc'; @@ -8,7 +8,7 @@ export class Dedupe implements Integration { /** * @inheritDoc */ - private previousEvent?: SentryEvent; + private previousEvent?: Event; /** * @inheritDoc @@ -24,7 +24,7 @@ export class Dedupe implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((currentEvent: SentryEvent) => { + addGlobalEventProcessor((currentEvent: Event) => { const self = getCurrentHub().getIntegration(Dedupe); if (self) { // Juuust in case something goes wrong @@ -43,7 +43,7 @@ export class Dedupe implements Integration { } /** JSDoc */ - public shouldDropEvent(currentEvent: SentryEvent, previousEvent?: SentryEvent): boolean { + public shouldDropEvent(currentEvent: Event, previousEvent?: Event): boolean { if (!previousEvent) { return false; } @@ -70,7 +70,7 @@ export class Dedupe implements Integration { } /** JSDoc */ - private isSameMessageEvent(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean { + private isSameMessageEvent(currentEvent: Event, previousEvent: Event): boolean { const currentMessage = currentEvent.message; const previousMessage = previousEvent.message; @@ -100,7 +100,7 @@ export class Dedupe implements Integration { } /** JSDoc */ - private getFramesFromEvent(event: SentryEvent): StackFrame[] | undefined { + private getFramesFromEvent(event: Event): StackFrame[] | undefined { const exception = event.exception; if (exception) { @@ -118,7 +118,7 @@ export class Dedupe implements Integration { } /** JSDoc */ - private isSameStacktrace(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean { + private isSameStacktrace(currentEvent: Event, previousEvent: Event): boolean { let currentFrames = this.getFramesFromEvent(currentEvent); let previousFrames = this.getFramesFromEvent(previousEvent); @@ -159,12 +159,12 @@ export class Dedupe implements Integration { } /** JSDoc */ - private getExceptionFromEvent(event: SentryEvent): SentryException | undefined { + private getExceptionFromEvent(event: Event): Exception | undefined { return event.exception && event.exception.values && event.exception.values[0]; } /** JSDoc */ - private isSameExceptionEvent(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean { + private isSameExceptionEvent(currentEvent: Event, previousEvent: Event): boolean { const previousException = this.getExceptionFromEvent(previousEvent); const currentException = this.getExceptionFromEvent(currentEvent); @@ -188,7 +188,7 @@ export class Dedupe implements Integration { } /** JSDoc */ - private isSameFingerprint(currentEvent: SentryEvent, previousEvent: SentryEvent): boolean { + private isSameFingerprint(currentEvent: Event, previousEvent: Event): boolean { let currentFingerprint = currentEvent.fingerprint; let previousFingerprint = previousEvent.fingerprint; diff --git a/packages/core/src/integrations/extraerrordata.ts b/packages/core/src/integrations/extraerrordata.ts index 650adeffa2a2..dd57657e20a7 100644 --- a/packages/core/src/integrations/extraerrordata.ts +++ b/packages/core/src/integrations/extraerrordata.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, SentryEvent, SentryEventHint } from '@sentry/types'; +import { Event, EventHint, Integration } from '@sentry/types'; import { isError, isString } from '@sentry/utils/is'; import { logger } from '@sentry/utils/logger'; import { safeNormalize } from '@sentry/utils/object'; @@ -27,7 +27,7 @@ export class ExtraErrorData implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent, hint?: SentryEventHint) => { + addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(ExtraErrorData); if (!self) { return event; @@ -37,9 +37,9 @@ export class ExtraErrorData implements Integration { } /** - * Attaches extracted information from the Error object to extra field in the SentryEvent + * Attaches extracted information from the Error object to extra field in the Event */ - public enhanceEventWithErrorData(event: SentryEvent, hint?: SentryEventHint): SentryEvent { + public enhanceEventWithErrorData(event: Event, hint?: EventHint): Event { if (!hint || !hint.originalException || !isError(hint.originalException)) { return event; } diff --git a/packages/core/src/integrations/functiontostring.ts b/packages/core/src/integrations/functiontostring.ts index dc22512deb20..e436fe7d0c8a 100644 --- a/packages/core/src/integrations/functiontostring.ts +++ b/packages/core/src/integrations/functiontostring.ts @@ -1,4 +1,4 @@ -import { Integration, SentryWrappedFunction } from '@sentry/types'; +import { Integration, WrappedFunction } from '@sentry/types'; let originalFunctionToString: () => void; @@ -20,7 +20,7 @@ export class FunctionToString implements Integration { public setupOnce(): void { originalFunctionToString = Function.prototype.toString; - Function.prototype.toString = function(this: SentryWrappedFunction, ...args: any[]): string { + Function.prototype.toString = function(this: WrappedFunction, ...args: any[]): string { const context = this.__sentry__ ? this.__sentry_original__ : this; // tslint:disable-next-line:no-unsafe-any return originalFunctionToString.apply(context, args); diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index ca62140405ca..2ce7a88f9934 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -1,10 +1,9 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Client, Event, Integration } from '@sentry/types'; import { isRegExp } from '@sentry/utils/is'; import { logger } from '@sentry/utils/logger'; import { getEventDescription } from '@sentry/utils/misc'; import { includes } from '@sentry/utils/string'; -import { Client } from '../interfaces'; // "Script error." is hard coded into browsers for errors that it can't read. // this is the result of a script being pulled in from an external domain and CORS. @@ -35,7 +34,7 @@ export class InboundFilters implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent) => { + addGlobalEventProcessor((event: Event) => { const hub = getCurrentHub(); if (!hub) { return event; @@ -54,7 +53,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - public shouldDropEvent(event: SentryEvent, options: InboundFiltersOptions): boolean { + public shouldDropEvent(event: Event, options: InboundFiltersOptions): boolean { if (this.isSentryError(event, options)) { logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); return true; @@ -85,7 +84,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - public isSentryError(event: SentryEvent, options: InboundFiltersOptions = {}): boolean { + public isSentryError(event: Event, options: InboundFiltersOptions = {}): boolean { if (!options.ignoreInternal) { return false; } @@ -99,7 +98,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - public isIgnoredError(event: SentryEvent, options: InboundFiltersOptions = {}): boolean { + public isIgnoredError(event: Event, options: InboundFiltersOptions = {}): boolean { if (!options.ignoreErrors || !options.ignoreErrors.length) { return false; } @@ -111,7 +110,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - public isBlacklistedUrl(event: SentryEvent, options: InboundFiltersOptions = {}): boolean { + public isBlacklistedUrl(event: Event, options: InboundFiltersOptions = {}): boolean { // TODO: Use Glob instead? if (!options.blacklistUrls || !options.blacklistUrls.length) { return false; @@ -121,7 +120,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - public isWhitelistedUrl(event: SentryEvent, options: InboundFiltersOptions = {}): boolean { + public isWhitelistedUrl(event: Event, options: InboundFiltersOptions = {}): boolean { // TODO: Use Glob instead? if (!options.whitelistUrls || !options.whitelistUrls.length) { return true; @@ -156,7 +155,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - private getPossibleEventMessages(event: SentryEvent): string[] { + private getPossibleEventMessages(event: Event): string[] { if (event.message) { return [event.message]; } else if (event.exception) { @@ -174,7 +173,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - private getEventFilterUrl(event: SentryEvent): string | null { + private getEventFilterUrl(event: Event): string | null { try { if (event.stacktrace) { // tslint:disable:no-unsafe-any diff --git a/packages/core/src/integrations/pluggable/debug.ts b/packages/core/src/integrations/pluggable/debug.ts index 4b440dd16e08..4af6aa19036c 100644 --- a/packages/core/src/integrations/pluggable/debug.ts +++ b/packages/core/src/integrations/pluggable/debug.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, SentryEvent, SentryEventHint } from '@sentry/types'; +import { Event, EventHint, Integration } from '@sentry/types'; /** JSDoc */ interface DebugOptions { @@ -37,7 +37,7 @@ export class Debug implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent, hint?: SentryEventHint) => { + addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(Debug); if (self) { // tslint:disable:no-console diff --git a/packages/core/src/integrations/pluggable/rewriteframes.ts b/packages/core/src/integrations/pluggable/rewriteframes.ts index c803d99ed5ea..18b965102db4 100644 --- a/packages/core/src/integrations/pluggable/rewriteframes.ts +++ b/packages/core/src/integrations/pluggable/rewriteframes.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; -import { Integration, SentryEvent, StackFrame } from '@sentry/types'; +import { Event, Integration, StackFrame } from '@sentry/types'; import { basename, relative } from '@sentry/utils/path'; type StackFrameIteratee = (frame: StackFrame) => StackFrame; @@ -58,7 +58,7 @@ export class RewriteFrames implements Integration { } /** JSDoc */ - public process(event: SentryEvent): SentryEvent { + public process(event: Event): Event { const frames = this.getFramesFromEvent(event); if (frames) { for (const i in frames) { @@ -70,7 +70,7 @@ export class RewriteFrames implements Integration { } /** JSDoc */ - private getFramesFromEvent(event: SentryEvent): StackFrame[] | undefined { + private getFramesFromEvent(event: Event): StackFrame[] | undefined { const exception = event.exception; if (exception) { diff --git a/packages/core/src/interfaces.ts b/packages/core/src/interfaces.ts deleted file mode 100644 index d7b875a503c6..000000000000 --- a/packages/core/src/interfaces.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { Scope } from '@sentry/hub'; -import { - Breadcrumb, - Integration, - IntegrationClass, - SentryBreadcrumbHint, - SentryEvent, - SentryEventHint, - Severity, - Transport, - TransportClass, - TransportOptions, -} from '@sentry/types'; -import { SyncPromise } from '@sentry/utils/syncpromise'; -import { Dsn } from './dsn'; - -/** Console logging verbosity for the SDK. */ -export enum LogLevel { - /** No logs will be generated. */ - None = 0, - /** Only SDK internal errors will be logged. */ - Error = 1, - /** Information useful for debugging the SDK will be logged. */ - Debug = 2, - /** All SDK actions will be logged. */ - Verbose = 3, -} - -/** Base configuration options for every SDK. */ -export interface Options { - /** - * Enable debug functionality in the SDK itself - */ - debug?: boolean; - - /** - * Specifies whether this SDK should activate and send events to Sentry. - * Disabling the SDK reduces all overhead from instrumentation, collecting - * breadcrumbs and capturing events. Defaults to true. - */ - enabled?: boolean; - - /** - * The Dsn used to connect to Sentry and identify the project. If omitted, the - * SDK will not send any data to Sentry. - */ - dsn?: string; - - /** - * If this is set to false, default integrations will not be added, otherwise this will internally be set to the - * recommended default integrations. - */ - defaultIntegrations?: false | Integration[]; - - /** - * List of integrations that should be installed after SDK was initialized. - * Accepts either a list of integrations or a function that receives - * default integrations and returns a new, updated list. - */ - integrations?: Integration[] | ((integrations: Integration[]) => Integration[]); - - /** - * A pattern for error messages which should not be sent to Sentry. - * By default, all errors will be sent. - */ - ignoreErrors?: Array; - - /** - * Transport object that should be used to send events to Sentry - */ - transport?: TransportClass; - - /** - * Options for the default transport that the SDK uses. - */ - transportOptions?: TransportOptions; - - /** - * The release identifier used when uploading respective source maps. Specify - * this value to allow Sentry to resolve the correct source maps when - * processing events. - */ - release?: string; - - /** The current environment of your application (e.g. "production"). */ - environment?: string; - - /** Sets the distribution for all events */ - dist?: string; - - /** The maximum number of breadcrumbs sent with events. Defaults to 100. */ - maxBreadcrumbs?: number; - - /** Console logging verbosity for the SDK Client. */ - logLevel?: LogLevel; - - /** A global sample rate to apply to all events (0 - 1). */ - sampleRate?: number; - - /** Attaches stacktraces to pure capture message / log integrations */ - attachStacktrace?: boolean; - - /** - * A callback invoked during event submission, allowing to optionally modify - * the event before it is sent to Sentry. - * - * Note that you must return a valid event from this callback. If you do not - * wish to modify the event, simply return it at the end. - * Returning null will case the event to be dropped. - * - * @param event The error or message event generated by the SDK. - * @param hint May contain additional information about the original exception. - * @returns A new event that will be sent | null. - */ - beforeSend?(event: SentryEvent, hint?: SentryEventHint): Promise | SentryEvent | null; - - /** - * A callback invoked when adding a breadcrumb, allowing to optionally modify - * it before adding it to future events. - * - * Note that you must return a valid breadcrumb from this callback. If you do - * not wish to modify the breadcrumb, simply return it at the end. - * Returning null will case the breadcrumb to be dropped. - * - * @param breadcrumb The breadcrumb as created by the SDK. - * @returns The breadcrumb that will be added | null. - */ - beforeBreadcrumb?(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint): Breadcrumb | null; -} - -/** - * User-Facing Sentry SDK Client Client. - * - * This interface contains all methods to interface with the SDK once it has - * been installed. It allows to send events to Sentry, record breadcrumbs and - * set a context included in every event. Since the SDK mutates its environment, - * there will only be one instance during runtime. To retrieve that instance, - * use {@link Client.getInstance}. - * - * Note that the call to {@link Client.install} should occur as early as - * possible so that even errors during startup can be recorded reliably: - * - * @example - * import { captureMessage } from '@sentry/node'; - * captureMessage('Custom message'); - */ -export interface Client { - /** - * Captures an exception event and sends it to Sentry. - * - * @param exception An exception-like object. - * @param hint May contain additional information about the original exception. - * @param scope An optional scope containing event metadata. - * @returns The event id - */ - captureException(exception: any, hint?: SentryEventHint, scope?: Scope): string | undefined; - - /** - * Captures a message event and sends it to Sentry. - * - * @param message The message to send to Sentry. - * @param level Define the level of the message. - * @param hint May contain additional information about the original exception. - * @param scope An optional scope containing event metadata. - * @returns The event id - */ - captureMessage(message: string, level?: Severity, hint?: SentryEventHint, scope?: Scope): string | undefined; - - /** - * Captures a manually created event and sends it to Sentry. - * - * @param event The event to send to Sentry. - * @param hint May contain additional information about the original exception. - * @param scope An optional scope containing event metadata. - * @returns The event id - */ - captureEvent(event: SentryEvent, hint?: SentryEventHint, scope?: Scope): string | undefined; - - /** - * Records a new breadcrumb which will be attached to future events. - * - * Breadcrumbs will be added to subsequent events to provide more context on - * user's actions prior to an error or crash. To configure the maximum number - * of breadcrumbs, use {@link Options.maxBreadcrumbs}. - * - * @param breadcrumb The breadcrumb to record. - * @param hint May contain additional information about the original breadcrumb. - * @param scope An optional scope to store this breadcrumb in. - */ - addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint, scope?: Scope): void; - - /** Returns the current Dsn. */ - getDsn(): Dsn | undefined; - - /** Returns the current options. */ - getOptions(): O; - - /** - * A promise that resolves when all current events have been sent. - * If you provide a timeout and the queue takes longer to drain the promise returns false. - * - * @param timeout Maximum time in ms the client should wait. - */ - close(timeout?: number): Promise; - - /** - * A promise that resolves when all current events have been sent. - * If you provide a timeout and the queue takes longer to drain the promise returns false. - * - * @param timeout Maximum time in ms the client should wait. - */ - flush(timeout?: number): Promise; - - /** Returns an array of installed integrations on the client. */ - getIntegration(integartion: IntegrationClass): T | null; -} - -/** - * Internal platform-dependent Sentry SDK Backend. - * - * While {@link Client} contains business logic specific to an SDK, the - * Backend offers platform specific implementations for low-level operations. - * These are persisting and loading information, sending events, and hooking - * into the environment. - * - * Backends receive a handle to the Client in their constructor. When a - * Backend automatically generates events or breadcrumbs, it must pass them to - * the Client for validation and processing first. - * - * Usually, the Client will be of corresponding type, e.g. NodeBackend - * receives NodeClient. However, higher-level SDKs can choose to instanciate - * multiple Backends and delegate tasks between them. In this case, an event - * generated by one backend might very well be sent by another one. - * - * The client also provides access to options via {@link Client.getOptions} - * and context via {@link Client.getContext}. Note that the user might update - * these any time and they should not be cached. - */ -export interface Backend { - /** Creates a {@link SentryEvent} from an exception. */ - eventFromException(exception: any, hint?: SentryEventHint): SyncPromise; - - /** Creates a {@link SentryEvent} from a plain message. */ - eventFromMessage(message: string, level?: Severity, hint?: SentryEventHint): SyncPromise; - - /** Submits the event to Sentry */ - sendEvent(event: SentryEvent): void; - - /** - * Receives a breadcrumb and stores it in a platform-dependent way. - * - * This function is invoked by the client before merging the breadcrumb into - * the scope. Return `false` to prevent this breadcrumb from being merged. - * This should be done for custom breadcrumb management in the backend. - * - * In most cases, this method does not have to perform any action and can - * simply return `true`. It can either be synchronous or asynchronous. - * - * @param breadcrumb The breadcrumb to store. - * @returns True if the breadcrumb should be merged by the client. - */ - storeBreadcrumb(breadcrumb: Breadcrumb): boolean; - - /** - * Receives the whole scope and stores it in a platform-dependent way. - * - * This function is invoked by the scope after the scope is configured. - * This should be done for custom context management in the backend. - * - * @param scope The scope to store. - */ - storeScope(scope: Scope): void; - - /** - * Returns the transport that is used by the backend. - * Please note that the transport gets lazy initialized so it will only be there once the first event has been sent. - * - * @returns The transport. - */ - getTransport(): Transport; -} diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index f760e65d2448..d698c34fbf08 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,6 +1,6 @@ import { getCurrentHub } from '@sentry/hub'; +import { Client, Options } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; -import { Client, Options } from './interfaces'; /** A class object that can instanciate Client objects. */ export interface ClientClass { diff --git a/packages/core/src/transports/noop.ts b/packages/core/src/transports/noop.ts index 65ff574a65f8..4bad9131885e 100644 --- a/packages/core/src/transports/noop.ts +++ b/packages/core/src/transports/noop.ts @@ -1,11 +1,11 @@ -import { SentryResponse, Status, Transport } from '@sentry/types'; +import { Response, Status, Transport } from '@sentry/types'; /** Noop transport */ export class NoopTransport implements Transport { /** * @inheritDoc */ - public async sendEvent(_: string): Promise { + public async sendEvent(_: string): Promise { return Promise.resolve({ reason: `NoopTransport: Event has been skipped because no Dsn is configured.`, status: Status.Skipped, diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 01fe7443c685..111ba84c92f5 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1,5 +1,5 @@ -import { Scope } from '@sentry/hub'; -import { SentryEvent } from '@sentry/types'; +import { Hub, Scope } from '@sentry/hub'; +import { Event } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import { TestBackend } from '../mocks/backend'; import { TestClient } from '../mocks/client'; @@ -76,8 +76,9 @@ describe('BaseClient', () => { expect.assertions(1); const client = new TestClient({}); const scope = new Scope(); + const hub = new Hub(client, scope); scope.addBreadcrumb({ message: 'hello' }, 100); - client.addBreadcrumb({ message: 'world' }, undefined, scope); + hub.addBreadcrumb({ message: 'world' }); expect((scope as any).breadcrumbs[1].message).toBe('world'); }); @@ -85,8 +86,9 @@ describe('BaseClient', () => { expect.assertions(1); const client = new TestClient({}); const scope = new Scope(); + const hub = new Hub(client, scope); scope.addBreadcrumb({ message: 'hello' }, 100); - client.addBreadcrumb({ message: 'world' }, undefined, scope); + hub.addBreadcrumb({ message: 'world' }); expect((scope as any).breadcrumbs[1].timestamp).toBeGreaterThan(1); }); @@ -94,8 +96,9 @@ describe('BaseClient', () => { expect.assertions(2); const client = new TestClient({ maxBreadcrumbs: 1 }); const scope = new Scope(); + const hub = new Hub(client, scope); scope.addBreadcrumb({ message: 'hello' }, 100); - client.addBreadcrumb({ message: 'world' }, undefined, scope); + hub.addBreadcrumb({ message: 'world' }); expect((scope as any).breadcrumbs.length).toBe(1); expect((scope as any).breadcrumbs[0].message).toBe('world'); }); @@ -104,8 +107,9 @@ describe('BaseClient', () => { expect.assertions(1); const client = new TestClient({}); const scope = new Scope(); - client.addBreadcrumb({ message: 'hello' }, undefined, scope); - client.addBreadcrumb({ message: 'world' }, undefined, scope); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: 'hello' }); + hub.addBreadcrumb({ message: 'world' }); expect((scope as any).breadcrumbs).toHaveLength(2); }); @@ -114,7 +118,8 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); const client = new TestClient({ beforeBreadcrumb }); const scope = new Scope(); - client.addBreadcrumb({ message: 'hello' }, undefined, scope); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: 'hello' }); expect((scope as any).breadcrumbs[0].message).toBe('hello'); }); @@ -123,7 +128,8 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); const client = new TestClient({ beforeBreadcrumb }); const scope = new Scope(); - client.addBreadcrumb({ message: 'hello' }, undefined, scope); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: 'hello' }); expect((scope as any).breadcrumbs[0].message).toBe('changed'); }); @@ -132,7 +138,8 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => null); const client = new TestClient({ beforeBreadcrumb }); const scope = new Scope(); - client.addBreadcrumb({ message: 'hello' }, undefined, scope); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: 'hello' }); expect((scope as any).breadcrumbs.length).toBe(0); }); @@ -141,7 +148,8 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); const client = new TestClient({ beforeBreadcrumb }); const scope = new Scope(); - client.addBreadcrumb({ message: 'hello' }, { data: 'someRandomThing' }, scope); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: 'hello' }, { data: 'someRandomThing' }); expect((scope as any).breadcrumbs[0].message).toBe('hello'); expect((scope as any).breadcrumbs[0].data).toBe('someRandomThing'); }); @@ -270,18 +278,15 @@ describe('BaseClient', () => { }); test('limits previously saved breadcrumbs', () => { - expect.assertions(1); + expect.assertions(2); const client = new TestClient({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); const scope = new Scope(); - scope.addBreadcrumb({ message: '1' }, 100); - scope.addBreadcrumb({ message: '2' }, 200); + const hub = new Hub(client, scope); + hub.addBreadcrumb({ message: '1' }); + hub.addBreadcrumb({ message: '2' }); client.captureEvent({ message: 'message' }, undefined, scope); - expect(TestBackend.instance!.event!).toEqual({ - breadcrumbs: [{ message: '2' }], - event_id: '42', - fingerprint: ['message'], - message: 'message', - }); + expect(TestBackend.instance!.event!.breadcrumbs).toHaveLength(1); + expect(TestBackend.instance!.event!.breadcrumbs![0].message).toEqual('2'); }); test('adds context data', () => { @@ -344,7 +349,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn( async event => - new Promise(resolve => { + new Promise(resolve => { setTimeout(() => { resolve(event); }, 1); @@ -353,7 +358,7 @@ describe('BaseClient', () => { const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); - TestBackend.sendEventCalled = (event: SentryEvent) => { + TestBackend.sendEventCalled = (event: Event) => { expect(event.message).toBe('hello'); }; setTimeout(() => { @@ -367,7 +372,7 @@ describe('BaseClient', () => { expect.assertions(1); const beforeSend = jest.fn( async () => - new Promise(resolve => { + new Promise(resolve => { setTimeout(() => { resolve({ message: 'changed2' }); }, 1); @@ -377,7 +382,7 @@ describe('BaseClient', () => { const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); - TestBackend.sendEventCalled = (event: SentryEvent) => { + TestBackend.sendEventCalled = (event: Event) => { expect(event.message).toBe('changed2'); }; setTimeout(() => { @@ -409,7 +414,7 @@ describe('BaseClient', () => { const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); expect(TestBackend.instance!.event!.message).toBe('hello'); - expect(TestBackend.instance!.event!.data).toBe('someRandomThing'); + expect((TestBackend.instance!.event! as any).data).toBe('someRandomThing'); }); }); diff --git a/packages/core/test/mocks/backend.ts b/packages/core/test/mocks/backend.ts index b4a741ff8c77..b97ae9876df1 100644 --- a/packages/core/test/mocks/backend.ts +++ b/packages/core/test/mocks/backend.ts @@ -1,7 +1,6 @@ -import { SentryEvent } from '@sentry/types'; +import { Event, Options } from '@sentry/types'; import { SyncPromise } from '@sentry/utils/syncpromise'; import { BaseBackend } from '../../src/basebackend'; -import { Options } from '../../src/interfaces'; export interface TestOptions extends Options { test?: boolean; @@ -10,23 +9,16 @@ export interface TestOptions extends Options { export class TestBackend extends BaseBackend { public static instance?: TestBackend; - public static sendEventCalled?: (event: SentryEvent) => void; + public static sendEventCalled?: (event: Event) => void; - public installed: number; - public event?: SentryEvent; + public event?: Event; public constructor(protected readonly options: TestOptions) { super(options); TestBackend.instance = this; - this.installed = 0; } - public install(): boolean { - this.installed += 1; - return !this.options.mockInstallFailure; - } - - public eventFromException(exception: any): SyncPromise { + public eventFromException(exception: any): SyncPromise { return SyncPromise.resolve({ exception: { values: [ @@ -39,11 +31,11 @@ export class TestBackend extends BaseBackend { }); } - public eventFromMessage(message: string): SyncPromise { + public eventFromMessage(message: string): SyncPromise { return SyncPromise.resolve({ message }); } - public sendEvent(event: SentryEvent): void { + public sendEvent(event: Event): void { this.event = event; // tslint:disable-next-line TestBackend.sendEventCalled && TestBackend.sendEventCalled(event); diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index a12e7a107e69..7e03da10281b 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -1,6 +1,6 @@ import { getCurrentHub } from '@sentry/hub'; import { configureScope } from '@sentry/minimal'; -import { Integration, SentryEvent } from '@sentry/types'; +import { Event, Integration } from '@sentry/types'; export class TestIntegration implements Integration { public name: string = 'TestIntegration'; @@ -8,7 +8,7 @@ export class TestIntegration implements Integration { public setupOnce(): void { configureScope(scope => { - scope.addEventProcessor((event: SentryEvent) => { + scope.addEventProcessor((event: Event) => { if (!getCurrentHub().getIntegration(TestIntegration)) { return event; } diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index ae49978cc283..9f7c90c6404a 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -1,14 +1,15 @@ import { Breadcrumb, + BreadcrumbHint, + Client, + Event, + EventHint, Integration, IntegrationClass, - SentryBreadcrumbHint, - SentryEvent, - SentryEventHint, Severity, } from '@sentry/types'; import { logger } from '@sentry/utils/logger'; -import { dynamicRequire, getGlobalObject, uuid4 } from '@sentry/utils/misc'; +import { consoleSandbox, dynamicRequire, getGlobalObject, uuid4 } from '@sentry/utils/misc'; import { Carrier, Layer } from './interfaces'; import { Scope } from './scope'; @@ -32,6 +33,18 @@ declare module 'domain' { */ export const API_VERSION = 3; +/** + * Default maximum number of breadcrumbs added to an event. Can be overwritten + * with {@link Options.maxBreadcrumbs}. + */ +const DEFAULT_BREADCRUMBS = 30; + +/** + * Absolute maximum number of breadcrumbs added to an event. The + * `maxBreadcrumbs` option cannot be higher than this value. + */ +const MAX_BREADCRUMBS = 100; + /** * Internal class used to make sure we always have the latest internal functions * working in case we have a version conflict. @@ -51,20 +64,20 @@ export class Hub { * @param scope bound to the hub. * @param version number, higher number means higher priority. */ - public constructor(client?: any, scope: Scope = new Scope(), private readonly version: number = API_VERSION) { + public constructor(client?: Client, scope: Scope = new Scope(), private readonly version: number = API_VERSION) { this.stack.push({ client, scope }); } /** * Internal helper function to call a method on the top client if it exists. * - * @param method The method to call on the client/client. - * @param args Arguments to pass to the client/frontend. + * @param method The method to call on the client. + * @param args Arguments to pass to the client function. */ - private invokeClient(method: string, ...args: any[]): void { + private invokeClient(method: M, ...args: any[]): void { const top = this.getStackTop(); if (top && top.client && top.client[method]) { - top.client[method](...args, top.scope); + (top.client as any)[method](...args, top.scope); } } @@ -84,20 +97,9 @@ export class Hub { * This binds the given client to the current scope. * @param client An SDK client (client) instance. */ - public bindClient(client?: any): void { + public bindClient(client?: Client): void { const top = this.getStackTop(); top.client = client; - if (top && top.scope && client) { - top.scope.addScopeListener((s: Scope) => { - if (client.getBackend) { - try { - client.getBackend().storeScope(s); - } catch { - // Do nothing - } - } - }); - } } /** @@ -182,7 +184,7 @@ export class Hub { * @param hint May contain additional information about the original exception. * @returns The generated eventId. */ - public captureException(exception: any, hint?: SentryEventHint): string { + public captureException(exception: any, hint?: EventHint): string { const eventId = (this._lastEventId = uuid4()); this.invokeClient('captureException', exception, { ...hint, @@ -199,7 +201,7 @@ export class Hub { * @param hint May contain additional information about the original exception. * @returns The generated eventId. */ - public captureMessage(message: string, level?: Severity, hint?: SentryEventHint): string { + public captureMessage(message: string, level?: Severity, hint?: EventHint): string { const eventId = (this._lastEventId = uuid4()); this.invokeClient('captureMessage', message, level, { ...hint, @@ -214,7 +216,7 @@ export class Hub { * @param event The event to send to Sentry. * @param hint May contain additional information about the original exception. */ - public captureEvent(event: SentryEvent, hint?: SentryEventHint): string { + public captureEvent(event: Event, hint?: EventHint): string { const eventId = (this._lastEventId = uuid4()); this.invokeClient('captureEvent', event, { ...hint, @@ -241,8 +243,30 @@ export class Hub { * @param breadcrumb The breadcrumb to record. * @param hint May contain additional information about the original breadcrumb. */ - public addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint): void { - this.invokeClient('addBreadcrumb', breadcrumb, { ...hint }); + public addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { + const top = this.getStackTop(); + + if (!top.scope || !top.client) { + return; + } + + const { beforeBreadcrumb, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = top.client.getOptions(); + + if (maxBreadcrumbs <= 0) { + return; + } + + const timestamp = new Date().getTime() / 1000; + const mergedBreadcrumb = { timestamp, ...breadcrumb }; + const finalBreadcrumb = beforeBreadcrumb + ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null) + : mergedBreadcrumb; + + if (finalBreadcrumb === null) { + return; + } + + top.scope.addBreadcrumb(finalBreadcrumb, Math.min(maxBreadcrumbs, MAX_BREADCRUMBS)); } /** diff --git a/packages/hub/src/interfaces.ts b/packages/hub/src/interfaces.ts index 963cb08240ab..a46d0e14555f 100644 --- a/packages/hub/src/interfaces.ts +++ b/packages/hub/src/interfaces.ts @@ -1,3 +1,4 @@ +import { Client } from '@sentry/types'; import { Hub } from './hub'; import { Scope } from './scope'; @@ -6,7 +7,7 @@ import { Scope } from './scope'; * @hidden */ export interface Layer { - client?: any; + client?: Client; scope?: Scope; } diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 1286537745ae..fbceaa7440dd 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -1,25 +1,14 @@ -import { Breadcrumb, SentryEvent, SentryEventHint, Severity, User } from '@sentry/types'; +import { Breadcrumb, Event, EventHint, EventProcessor, Scope as ScopeInterface, Severity, User } from '@sentry/types'; import { isFunction, isThenable } from '@sentry/utils/is'; import { getGlobalObject } from '@sentry/utils/misc'; import { assign, safeNormalize } from '@sentry/utils/object'; import { SyncPromise } from '@sentry/utils/syncpromise'; -/** - * Event processors are used to change the event before it will be send. - * We strongly advise to make this function sync. - * Returning a Promise will work just fine, but better be sure that you know what you are doing. - * Event processing will be deferred until your Promise is resolved. - */ -export type EventProcessor = ( - event: SentryEvent, - hint?: SentryEventHint, -) => Promise | SentryEvent | null; - /** * Holds additional event information. {@link Scope.applyToEvent} will be * called by the client before an event will be sent. */ -export class Scope { +export class Scope implements ScopeInterface { /** Flag if notifiying is happening. */ protected notifyingListeners: boolean = false; @@ -52,44 +41,31 @@ export class Scope { this.scopeListeners.push(callback); } - /** Add new event processor that will be called after {@link applyToEvent}. */ + /** + * @inheritdoc + */ public addEventProcessor(callback: EventProcessor): Scope { this.eventProcessors.push(callback); return this; } - /** - * This will be called on every set call. - */ - protected notifyScopeListeners(): void { - if (!this.notifyingListeners) { - this.notifyingListeners = true; - setTimeout(() => { - this.scopeListeners.forEach(callback => { - callback(this); - }); - this.notifyingListeners = false; - }); - } - } - /** * This will be called after {@link applyToEvent} is finished. */ protected notifyEventProcessors( processors: EventProcessor[], - event: SentryEvent | null, - hint?: SentryEventHint, + event: Event | null, + hint?: EventHint, index: number = 0, - ): SyncPromise { - return new SyncPromise((resolve, reject) => { + ): SyncPromise { + return new SyncPromise((resolve, reject) => { const processor = processors[index]; if (event === null || !isFunction(processor)) { resolve(event); } else { - const result = processor({ ...event }, hint) as SentryEvent | null; + const result = processor({ ...event }, hint) as Event | null; if (isThenable(result)) { - (result as Promise) + (result as Promise) .then(final => this.notifyEventProcessors(processors, final, hint, index + 1).then(resolve)) .catch(reject); } else { @@ -102,52 +78,42 @@ export class Scope { } /** - * Updates user context information for future events. - * @param user User context object to be set in the current context. + * @inheritdoc */ public setUser(user: User): Scope { this.user = safeNormalize(user); - this.notifyScopeListeners(); return this; } /** - * Updates tags context information for future events. - * @param tags Tags context object to merge into current context. + * @inheritdoc */ public setTag(key: string, value: string): Scope { this.tags = { ...this.tags, [key]: safeNormalize(value) }; - this.notifyScopeListeners(); return this; } /** - * Updates extra context information for future events. - * @param extra context object to merge into current context. + * @inheritdoc */ public setExtra(key: string, extra: any): Scope { this.extra = { ...this.extra, [key]: safeNormalize(extra) }; - this.notifyScopeListeners(); return this; } /** - * Sets the fingerprint on the scope to send with the events. - * @param fingerprint string[] to group events in Sentry. + * @inheritdoc */ public setFingerprint(fingerprint: string[]): Scope { this.fingerprint = safeNormalize(fingerprint); - this.notifyScopeListeners(); return this; } /** - * Sets the level on the scope for future events. - * @param level string {@link Severity} + * @inheritdoc */ public setLevel(level: Severity): Scope { this.level = safeNormalize(level); - this.notifyScopeListeners(); return this; } @@ -169,7 +135,9 @@ export class Scope { return newScope; } - /** Clears the current scope and resets its properties. */ + /** + * @inheritdoc + */ public clear(): void { this.breadcrumbs = []; this.tags = {}; @@ -177,27 +145,23 @@ export class Scope { this.user = {}; this.level = undefined; this.fingerprint = undefined; - this.notifyScopeListeners(); } /** - * Sets the breadcrumbs in the scope - * @param breadcrumbs Breadcrumb - * @param maxBreadcrumbs number of max breadcrumbs to merged into event. + * @inheritdoc */ public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): void { this.breadcrumbs = maxBreadcrumbs !== undefined && maxBreadcrumbs >= 0 ? [...this.breadcrumbs, safeNormalize(breadcrumb)].slice(-maxBreadcrumbs) : [...this.breadcrumbs, safeNormalize(breadcrumb)]; - this.notifyScopeListeners(); } /** * Applies fingerprint from the scope to the event if there's one, * uses message if there's one instead or get rid of empty fingerprint */ - private applyFingerprint(event: SentryEvent): void { + private applyFingerprint(event: Event): void { // Make sure it's an array first and we actually have something in place event.fingerprint = event.fingerprint ? Array.isArray(event.fingerprint) @@ -223,16 +187,12 @@ export class Scope { * Applies the current context and fingerprint to the event. * Note that breadcrumbs will be added by the client. * Also if the event has already breadcrumbs on it, we do not merge them. - * @param event SentryEvent + * @param event Event * @param hint May contain additional informartion about the original exception. * @param maxBreadcrumbs number of max breadcrumbs to merged into event. * @hidden */ - public applyToEvent( - event: SentryEvent, - hint?: SentryEventHint, - maxBreadcrumbs?: number, - ): SyncPromise { + public applyToEvent(event: Event, hint?: EventHint): SyncPromise { if (this.extra && Object.keys(this.extra).length) { event.extra = { ...this.extra, ...event.extra }; } @@ -250,10 +210,7 @@ export class Scope { const hasNoBreadcrumbs = !event.breadcrumbs || event.breadcrumbs.length === 0; if (hasNoBreadcrumbs && this.breadcrumbs.length > 0) { - event.breadcrumbs = - maxBreadcrumbs !== undefined && maxBreadcrumbs >= 0 - ? this.breadcrumbs.slice(-maxBreadcrumbs) - : this.breadcrumbs; + event.breadcrumbs = this.breadcrumbs; } return this.notifyEventProcessors([...getGlobalEventProcessors(), ...this.eventProcessors], event, hint); diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 75e455c9261b..a3ed9a3693ec 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,9 +1,7 @@ -import { SentryEvent } from '@sentry/types'; -import { logger } from '@sentry/utils/logger'; +import { Event } from '@sentry/types'; import { getCurrentHub, Hub, Scope } from '../src'; -const clientFn = jest.fn(); -const scope = new Scope(); +const clientFn: any = jest.fn(); describe('Hub', () => { afterEach(() => { @@ -17,30 +15,12 @@ describe('Hub', () => { }); test('pass in filled layer', () => { - const hub = new Hub({ - clientFn, - }); + const hub = new Hub(clientFn); expect(hub.getStack()).toHaveLength(1); }); - test('invoke client sync', () => { - const hub = new Hub( - { - clientFn, - }, - scope, - ); - // @ts-ignore - hub.invokeClient('clientFn', true); - expect(clientFn).toHaveBeenCalled(); - expect(clientFn.mock.calls[0][0]).toBe(true); - expect(clientFn.mock.calls[0][1]).toBe(scope); - }); - test("don't invoke client sync with wrong func", () => { - const hub = new Hub({ - clientFn, - }); + const hub = new Hub(clientFn); // @ts-ignore hub.invokeClient('funca', true); expect(clientFn).not.toHaveBeenCalled(); @@ -62,7 +42,7 @@ describe('Hub', () => { }); test('pushScope inherit client', () => { - const testClient = { bla: 'a' }; + const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); hub.pushScope(); expect(hub.getStack()).toHaveLength(2); @@ -70,9 +50,9 @@ describe('Hub', () => { }); test('pushScope bindClient', () => { - const testClient = { bla: 'a' }; + const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); - const ndClient = { foo: 'bar' }; + const ndClient: any = { foo: 'bar' }; hub.pushScope(); hub.bindClient(ndClient); expect(hub.getStack()).toHaveLength(2); @@ -98,71 +78,29 @@ describe('Hub', () => { test('withScope bindClient', () => { const hub = new Hub(); - const testClient = { bla: 'a' }; - hub.withScope(() => { - hub.bindClient(testClient); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[1].client).toBe(testClient); - }); - expect(hub.getStack()).toHaveLength(1); - }); - - test('withScope bindClient scope changes', () => { - jest.useFakeTimers(); - const hub = new Hub(); - const storeScope = jest.fn(); - const testClient = { - bla: 'a', - getBackend: () => ({ storeScope }), - }; + const testClient: any = { bla: 'a' }; hub.withScope(() => { hub.bindClient(testClient); - hub.configureScope(localScope => { - localScope.setExtra('a', 'b'); - }); - jest.runAllTimers(); expect(hub.getStack()).toHaveLength(2); expect(hub.getStack()[1].client).toBe(testClient); - expect(storeScope.mock.calls).toHaveLength(1); - expect(storeScope.mock.calls[0][0].extra).toEqual({ a: 'b' }); - }); - expect(hub.getStack()).toHaveLength(1); - }); - - test('withScope bindClient scope changes without configureScope', () => { - jest.useFakeTimers(); - const hub = new Hub(); - const storeScope = jest.fn(); - const testClient = { - bla: 'a', - getBackend: () => ({ storeScope }), - }; - hub.withScope(localScope => { - hub.bindClient(testClient); - localScope.setExtra('a', 'b'); - jest.runAllTimers(); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[1].client).toBe(testClient); - expect(storeScope.mock.calls).toHaveLength(1); - expect(storeScope.mock.calls[0][0].extra).toEqual({ a: 'b' }); }); expect(hub.getStack()).toHaveLength(1); }); test('getCurrentClient', () => { - const testClient = { bla: 'a' }; + const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); expect(hub.getClient()).toBe(testClient); }); test('getStack', () => { - const client = { a: 'b' }; + const client: any = { a: 'b' }; const hub = new Hub(client); expect(hub.getStack()[0].client).toBe(client); }); test('getStackTop', () => { - const testClient = { bla: 'a' }; + const testClient: any = { bla: 'a' }; const hub = new Hub(); hub.pushScope(); hub.pushScope(); @@ -189,7 +127,7 @@ describe('Hub', () => { }); test('captureEvent', () => { - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const hub = new Hub(); @@ -200,15 +138,6 @@ describe('Hub', () => { expect(spy.mock.calls[0][1]).toBe(event); }); - test('addBreadcrumb', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, 'invokeClient'); - hub.addBreadcrumb({ message: 'test' }); - expect(spy).toHaveBeenCalled(); - expect(spy.mock.calls[0][0]).toBe('addBreadcrumb'); - expect(spy.mock.calls[0][1]).toEqual({ message: 'test' }); - }); - test('configureScope', () => { expect.assertions(0); const hub = new Hub(); @@ -221,7 +150,7 @@ describe('Hub', () => { expect.assertions(1); const localScope = new Scope(); localScope.setExtra('a', 'b'); - const hub = new Hub({ a: 'b' }, localScope); + const hub = new Hub({ a: 'b' } as any, localScope); hub.configureScope(confScope => { expect((confScope as any).extra).toEqual({ a: 'b' }); }); @@ -229,14 +158,14 @@ describe('Hub', () => { test('pushScope inherit processors', () => { expect.assertions(1); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); - const hub = new Hub({ a: 'b' }, localScope); + const hub = new Hub({ a: 'b' } as any, localScope); - localScope.addEventProcessor(async (processedEvent: SentryEvent) => { + localScope.addEventProcessor(async (processedEvent: Event) => { processedEvent.dist = '1'; return processedEvent; }); @@ -264,7 +193,7 @@ describe('Hub', () => { }); test('captureEvent should set event_id in hint', () => { - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const hub = new Hub(); @@ -274,7 +203,7 @@ describe('Hub', () => { }); test('lastEventId should be the same as last created', () => { - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const hub = new Hub(); @@ -285,7 +214,7 @@ describe('Hub', () => { test('run', () => { const currentHub = getCurrentHub(); const myScope = new Scope(); - const myClient = { a: 'b' }; + const myClient: any = { a: 'b' }; myScope.setExtra('a', 'b'); const myHub = new Hub(myClient, myScope); myHub.run(hub => { diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 7b4c5fc72e1a..ceb18df98695 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,4 +1,4 @@ -import { SentryEvent, SentryEventHint, Severity } from '@sentry/types'; +import { Event, EventHint, Severity } from '@sentry/types'; import { Scope } from '../src'; describe('Scope', () => { @@ -75,17 +75,6 @@ describe('Scope', () => { expect((scope as any).extra).toEqual({ a: 2 }); }); - test('listeners', () => { - jest.useFakeTimers(); - const scope = new Scope(); - const listener = jest.fn(); - scope.addScopeListener(listener); - scope.setExtra('a', 2); - jest.runAllTimers(); - expect(listener).toHaveBeenCalled(); - expect(listener.mock.calls[0][0].extra).toEqual({ a: 2 }); - }); - test('applyToEvent', () => { expect.assertions(6); const scope = new Scope(); @@ -95,7 +84,7 @@ describe('Scope', () => { scope.setFingerprint(['abcd']); scope.setLevel(Severity.Warning); scope.addBreadcrumb({ message: 'test' }, 100); - const event: SentryEvent = {}; + const event: Event = {}; return scope.applyToEvent(event).then(processedEvent => { expect(processedEvent!.extra).toEqual({ a: 2 }); expect(processedEvent!.tags).toEqual({ a: 'b' }); @@ -114,7 +103,7 @@ describe('Scope', () => { scope.setUser({ id: '1' }); scope.setFingerprint(['abcd']); scope.addBreadcrumb({ message: 'test' }, 100); - const event: SentryEvent = { + const event: Event = { breadcrumbs: [{ message: 'test2' }], extra: { b: 3 }, fingerprint: ['efgh'], @@ -133,7 +122,7 @@ describe('Scope', () => { test('applyToEvent message fingerprint', async () => { expect.assertions(1); const scope = new Scope(); - const event: SentryEvent = { + const event: Event = { fingerprint: ['bar'], message: 'foo', }; @@ -146,7 +135,7 @@ describe('Scope', () => { expect.assertions(1); const scope = new Scope(); scope.setLevel(Severity.Warning); - const event: SentryEvent = {}; + const event: Event = {}; event.level = Severity.Critical; return scope.applyToEvent(event).then(processedEvent => { expect(processedEvent!.level).toEqual('warning'); @@ -167,20 +156,20 @@ describe('Scope', () => { test('addEventProcessor', () => { expect.assertions(3); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { processedEvent.dist = '1'; return processedEvent; }); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { expect(processedEvent.dist).toEqual('1'); return processedEvent; }); @@ -193,20 +182,20 @@ describe('Scope', () => { test('addEventProcessor async', async () => { jest.useFakeTimers(); expect.assertions(6); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); const callCounter = jest.fn(); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { callCounter(1); expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); localScope.addEventProcessor( - async (processedEvent: SentryEvent) => - new Promise(resolve => { + async (processedEvent: Event) => + new Promise(resolve => { callCounter(2); setTimeout(() => { callCounter(3); @@ -216,7 +205,7 @@ describe('Scope', () => { jest.runAllTimers(); }), ); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { callCounter(4); return processedEvent; }); @@ -233,27 +222,27 @@ describe('Scope', () => { test('addEventProcessor async with reject', async () => { jest.useFakeTimers(); expect.assertions(2); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); const callCounter = jest.fn(); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { callCounter(1); expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); localScope.addEventProcessor( - async (_processedEvent: SentryEvent) => - new Promise((_, reject) => { + async (_processedEvent: Event) => + new Promise((_, reject) => { setTimeout(() => { reject('bla'); }, 1); jest.runAllTimers(); }), ); - localScope.addEventProcessor((processedEvent: SentryEvent) => { + localScope.addEventProcessor((processedEvent: Event) => { callCounter(4); return processedEvent; }); @@ -265,12 +254,12 @@ describe('Scope', () => { test('addEventProcessor return null', () => { expect.assertions(1); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); - localScope.addEventProcessor(async (_: SentryEvent) => null); + localScope.addEventProcessor(async (_: Event) => null); return localScope.applyToEvent(event).then(processedEvent => { expect(processedEvent).toBeNull(); }); @@ -278,12 +267,12 @@ describe('Scope', () => { test('addEventProcessor pass along hint', () => { expect.assertions(3); - const event: SentryEvent = { + const event: Event = { extra: { b: 3 }, }; const localScope = new Scope(); localScope.setExtra('a', 'b'); - localScope.addEventProcessor(async (internalEvent: SentryEvent, hint?: SentryEventHint) => { + localScope.addEventProcessor(async (internalEvent: Event, hint?: EventHint) => { expect(hint).toBeTruthy(); expect(hint!.syntheticException).toBeTruthy(); return internalEvent; diff --git a/packages/minimal/src/index.ts b/packages/minimal/src/index.ts index 2f1c89a5ba2e..53048a982701 100644 --- a/packages/minimal/src/index.ts +++ b/packages/minimal/src/index.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Hub, Scope } from '@sentry/hub'; -import { Breadcrumb, SentryEvent, Severity } from '@sentry/types'; +import { Breadcrumb, Event, Severity } from '@sentry/types'; /** * This calls a function on the current hub. @@ -60,7 +60,7 @@ export function captureMessage(message: string, level?: Severity): string { * @param event The event to send to Sentry. * @returns The generated eventId. */ -export function captureEvent(event: SentryEvent): string { +export function captureEvent(event: Event): string { return callOnHub('captureEvent', event); } diff --git a/packages/minimal/test/lib/minimal.test.ts b/packages/minimal/test/lib/minimal.test.ts index e0d9b585cd0d..096366be557f 100644 --- a/packages/minimal/test/lib/minimal.test.ts +++ b/packages/minimal/test/lib/minimal.test.ts @@ -1,14 +1,6 @@ import { getCurrentHub, getHubFromCarrier, Scope } from '@sentry/hub'; import { Severity } from '@sentry/types'; -import { - _callOnClient, - addBreadcrumb, - captureEvent, - captureException, - captureMessage, - configureScope, - withScope, -} from '../../src'; +import { _callOnClient, captureEvent, captureException, captureMessage, configureScope, withScope } from '../../src'; import { init, TestClient, TestClient2 } from '../mocks/client'; declare var global: any; @@ -22,7 +14,7 @@ describe('Minimal', () => { describe('Capture', () => { test('Return an event_id', () => { - const client = { + const client: any = { captureException: jest.fn(async () => Promise.resolve()), }; getCurrentHub().withScope(() => { @@ -34,7 +26,7 @@ describe('Minimal', () => { }); test('Exception', () => { - const client = { + const client: any = { captureException: jest.fn(async () => Promise.resolve()), }; getCurrentHub().withScope(() => { @@ -46,7 +38,7 @@ describe('Minimal', () => { }); test('Message', () => { - const client = { captureMessage: jest.fn(async () => Promise.resolve()) }; + const client: any = { captureMessage: jest.fn(async () => Promise.resolve()) }; getCurrentHub().withScope(() => { getCurrentHub().bindClient(client); const message = 'yo'; @@ -56,7 +48,7 @@ describe('Minimal', () => { }); test('Event', () => { - const client = { captureEvent: jest.fn(async () => Promise.resolve()) }; + const client: any = { captureEvent: jest.fn(async () => Promise.resolve()) }; getCurrentHub().withScope(() => { getCurrentHub().bindClient(client); const e = { message: 'test' }; @@ -68,7 +60,7 @@ describe('Minimal', () => { describe('configureScope', () => { test('User Context', () => { - const client = new TestClient({}); + const client: any = new TestClient({}); getCurrentHub().pushScope(); getCurrentHub().bindClient(client); configureScope((scope: Scope) => { @@ -81,7 +73,7 @@ describe('Minimal', () => { }); test('Extra Context', () => { - const client = new TestClient({}); + const client: any = new TestClient({}); getCurrentHub().pushScope(); getCurrentHub().bindClient(client); configureScope((scope: Scope) => { @@ -104,7 +96,7 @@ describe('Minimal', () => { }); test('Fingerprint', () => { - const client = new TestClient({}); + const client: any = new TestClient({}); getCurrentHub().pushScope(); getCurrentHub().bindClient(client); configureScope((scope: Scope) => { @@ -114,7 +106,7 @@ describe('Minimal', () => { }); test('Level', () => { - const client = new TestClient({}); + const client: any = new TestClient({}); const scope = getCurrentHub().pushScope(); getCurrentHub().bindClient(client); scope.setLevel(Severity.Warning); @@ -123,7 +115,7 @@ describe('Minimal', () => { }); test('Clear Scope', () => { - const client = new TestClient({}); + const client: any = new TestClient({}); getCurrentHub().withScope(() => { getCurrentHub().bindClient(client); expect(global.__SENTRY__.hub.stack.length).toBe(2); @@ -140,19 +132,6 @@ describe('Minimal', () => { }); }); - test('Add Breadcrumb', () => { - const client = { - addBreadcrumb: jest.fn(), - }; - getCurrentHub().pushScope(); - getCurrentHub().bindClient(client); - addBreadcrumb({ message: 'world' }); - expect(client.addBreadcrumb.mock.calls[0][0]).toEqual({ - message: 'world', - }); - getCurrentHub().popScope(); - }); - test('returns undefined before binding a client', () => { expect(getCurrentHub().getClient()).toBeUndefined(); }); @@ -165,7 +144,7 @@ describe('Minimal', () => { test('Calls function on the client', done => { const s = jest.spyOn(TestClient.prototype, 'mySecretPublicMethod'); getCurrentHub().withScope(() => { - getCurrentHub().bindClient(new TestClient({})); + getCurrentHub().bindClient(new TestClient({}) as any); _callOnClient('mySecretPublicMethod', 'test'); expect(s.mock.calls[0][0]).toBe('test'); s.mockRestore(); @@ -177,7 +156,7 @@ describe('Minimal', () => { init({}); expect(() => { getCurrentHub().withScope(() => { - getCurrentHub().bindClient(new TestClient2()); + getCurrentHub().bindClient(new TestClient2() as any); }); }).not.toThrow(); }); @@ -186,7 +165,7 @@ describe('Minimal', () => { init({}); expect(() => { getCurrentHub().withScope(() => { - getCurrentHub().bindClient(new TestClient({})); + getCurrentHub().bindClient(new TestClient({}) as any); }); }).not.toThrow(); }); @@ -197,11 +176,11 @@ describe('Minimal', () => { }; const hub = getHubFromCarrier(iAmSomeGlobalVarTheUserHasToManage.state); hub.pushScope(); - hub.bindClient(new TestClient({})); + hub.bindClient(new TestClient({}) as any); hub.configureScope((scope: Scope) => { scope.setUser({ id: '1234' }); }); - expect(((iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub.stack[1] as any).scope.user).toEqual({ + expect((iAmSomeGlobalVarTheUserHasToManage.state as any).__SENTRY__.hub.stack[1].scope.user).toEqual({ id: '1234', }); hub.popScope(); diff --git a/packages/minimal/test/mocks/client.ts b/packages/minimal/test/mocks/client.ts index bcb38fa27eae..2e04f2645bca 100644 --- a/packages/minimal/test/mocks/client.ts +++ b/packages/minimal/test/mocks/client.ts @@ -15,5 +15,5 @@ export class TestClient { export class TestClient2 {} export function init(options: object): void { - getCurrentHub().bindClient(new TestClient(options)); + getCurrentHub().bindClient(new TestClient(options) as any); } diff --git a/packages/minimal/test/tslint.json b/packages/minimal/test/tslint.json index fc0ed75cfcd7..f2c15a212b8f 100644 --- a/packages/minimal/test/tslint.json +++ b/packages/minimal/test/tslint.json @@ -5,6 +5,7 @@ "max-classes-per-file": false, "no-non-null-assertion": false, "no-implicit-dependencies": [true, "dev"], - "no-unused-expression": false + "no-unused-expression": false, + "no-unsafe-any": false } } diff --git a/packages/node/src/backend.ts b/packages/node/src/backend.ts index e42af177d50c..c6e9033f6c20 100644 --- a/packages/node/src/backend.ts +++ b/packages/node/src/backend.ts @@ -1,5 +1,5 @@ -import { BaseBackend, Dsn, getCurrentHub, Options } from '@sentry/core'; -import { SentryEvent, SentryEventHint, Severity, Transport } from '@sentry/types'; +import { BaseBackend, Dsn, getCurrentHub } from '@sentry/core'; +import { Event, EventHint, Options, Severity, Transport } from '@sentry/types'; import { isError, isPlainObject } from '@sentry/utils/is'; import { limitObjectDepthToSize, serializeKeysToEventMessage } from '@sentry/utils/object'; import { SyncPromise } from '@sentry/utils/syncpromise'; @@ -73,7 +73,7 @@ export class NodeBackend extends BaseBackend { /** * @inheritDoc */ - public eventFromException(exception: any, hint?: SentryEventHint): SyncPromise { + public eventFromException(exception: any, hint?: EventHint): SyncPromise { let ex: any = exception; if (!isError(exception)) { @@ -101,7 +101,7 @@ export class NodeBackend extends BaseBackend { } } - return new SyncPromise(resolve => + return new SyncPromise(resolve => parseError(ex as Error, this.options).then(event => { resolve({ ...event, @@ -114,18 +114,14 @@ export class NodeBackend extends BaseBackend { /** * @inheritDoc */ - public eventFromMessage( - message: string, - level: Severity = Severity.Info, - hint?: SentryEventHint, - ): SyncPromise { - const event: SentryEvent = { + public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): SyncPromise { + const event: Event = { event_id: hint && hint.event_id, level, message, }; - return new SyncPromise(resolve => { + return new SyncPromise(resolve => { if (this.options.attachStacktrace && hint && hint.syntheticException) { const stack = hint.syntheticException ? extractStackFromError(hint.syntheticException) : []; parseStack(stack, this.options).then(frames => { diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index df295e677d80..b01b6d9cf253 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,5 +1,5 @@ import { BaseClient, Scope } from '@sentry/core'; -import { SentryEvent, SentryEventHint } from '@sentry/types'; +import { Event, EventHint } from '@sentry/types'; import { SyncPromise } from '@sentry/utils/syncpromise'; import { NodeBackend, NodeOptions } from './backend'; import { SDK_NAME, SDK_VERSION } from './version'; @@ -22,7 +22,7 @@ export class NodeClient extends BaseClient { /** * @inheritDoc */ - protected prepareEvent(event: SentryEvent, scope?: Scope, hint?: SentryEventHint): SyncPromise { + protected prepareEvent(event: Event, scope?: Scope, hint?: EventHint): SyncPromise { event.platform = event.platform || 'node'; event.sdk = { ...event.sdk, diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 9d0433f684fb..ecb59603b155 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -1,5 +1,5 @@ import { captureException, getCurrentHub } from '@sentry/core'; -import { SentryEvent } from '@sentry/types'; +import { Event } from '@sentry/types'; import { forget } from '@sentry/utils/async'; import { logger } from '@sentry/utils/logger'; import { serialize } from '@sentry/utils/object'; @@ -155,7 +155,7 @@ function extractUserData(req: { [key: string]: any }, keys: boolean | string[]): * @hidden */ export function parseRequest( - event: SentryEvent, + event: Event, req: { [key: string]: any; }, @@ -166,7 +166,7 @@ export function parseRequest( user?: boolean | string[]; version?: boolean; }, -): SentryEvent { +): Event { // tslint:disable-next-line:no-parameter-reassignment options = { request: true, @@ -234,7 +234,7 @@ export function requestHandler(options?: { local.on('error', next); local.run(() => { getCurrentHub().configureScope(scope => - scope.addEventProcessor((event: SentryEvent) => parseRequest(event, req, options)), + scope.addEventProcessor((event: Event) => parseRequest(event, req, options)), ); next(); }); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 1d93d0786a14..684c10c5090e 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -2,9 +2,9 @@ export { Breadcrumb, Request, SdkInfo, - SentryEvent, - SentryException, - SentryResponse, + Event, + Exception, + Response, Severity, StackFrame, Stacktrace, diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index df7395aabb5a..b43f8609633b 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Integration, SentryEvent, SentryEventHint, SentryException } from '@sentry/types'; +import { Event, EventHint, Exception, Integration } from '@sentry/types'; import { SyncPromise } from '@sentry/utils/syncpromise'; import { getExceptionFromError } from '../parsers'; @@ -46,10 +46,10 @@ export class LinkedErrors implements Integration { * @inheritDoc */ public setupOnce(): void { - addGlobalEventProcessor((event: SentryEvent, hint?: SentryEventHint) => { + addGlobalEventProcessor((event: Event, hint?: EventHint) => { const self = getCurrentHub().getIntegration(LinkedErrors); if (self) { - return (self.handler(event, hint) as unknown) as Promise; + return (self.handler(event, hint) as unknown) as Promise; } return event; }); @@ -58,13 +58,13 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public handler(event: SentryEvent, hint?: SentryEventHint): SyncPromise { + public handler(event: Event, hint?: EventHint): SyncPromise { if (!event.exception || !event.exception.values || !hint || !(hint.originalException instanceof Error)) { return SyncPromise.resolve(event); } - return new SyncPromise(resolve => { - this.walkErrorTree(hint.originalException as ExtendedError, this.key).then((linkedErrors: SentryException[]) => { + return new SyncPromise(resolve => { + this.walkErrorTree(hint.originalException as ExtendedError, this.key).then((linkedErrors: Exception[]) => { if (event && event.exception) { event.exception.values = [...linkedErrors, ...event.exception.values]; } @@ -76,16 +76,12 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - public walkErrorTree( - error: ExtendedError, - key: string, - stack: SentryException[] = [], - ): SyncPromise { + public walkErrorTree(error: ExtendedError, key: string, stack: Exception[] = []): SyncPromise { if (!(error[key] instanceof Error) || stack.length + 1 >= this.limit) { return SyncPromise.resolve(stack); } - return new SyncPromise(resolve => { - getExceptionFromError(error[key]).then((exception: SentryException) => { + return new SyncPromise(resolve => { + getExceptionFromError(error[key]).then((exception: Exception) => { this.walkErrorTree(error[key], key, [exception, ...stack]).then(resolve); }); }); diff --git a/packages/node/src/integrations/pluggable/transaction.ts b/packages/node/src/integrations/pluggable/transaction.ts index 1944c5b1e12c..5a87606ae8e0 100644 --- a/packages/node/src/integrations/pluggable/transaction.ts +++ b/packages/node/src/integrations/pluggable/transaction.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { Integration, SentryEvent, StackFrame } from '@sentry/types'; +import { Event, Integration, StackFrame } from '@sentry/types'; /** Add node transaction to the event */ export class Transaction implements Integration { @@ -28,7 +28,7 @@ export class Transaction implements Integration { /** * @inheritDoc */ - public process(event: SentryEvent): SentryEvent { + public process(event: Event): Event { const frames = this.getFramesFromEvent(event); // use for loop so we don't have to reverse whole frames array @@ -45,7 +45,7 @@ export class Transaction implements Integration { } /** JSDoc */ - private getFramesFromEvent(event: SentryEvent): StackFrame[] { + private getFramesFromEvent(event: Event): StackFrame[] { const exception = event.exception && event.exception.values && event.exception.values[0]; return (exception && exception.stacktrace && exception.stacktrace.frames) || []; } diff --git a/packages/node/src/parsers.ts b/packages/node/src/parsers.ts index 6c715321c1e6..6e15bd890464 100644 --- a/packages/node/src/parsers.ts +++ b/packages/node/src/parsers.ts @@ -1,4 +1,4 @@ -import { SentryEvent, SentryException, StackFrame } from '@sentry/types'; +import { Event, Exception, StackFrame } from '@sentry/types'; import { basename, dirname } from '@sentry/utils/path'; import { snipLine } from '@sentry/utils/string'; import { SyncPromise } from '@sentry/utils/syncpromise'; @@ -236,10 +236,10 @@ function addPrePostContext( /** * @hidden */ -export function getExceptionFromError(error: Error, options?: NodeOptions): SyncPromise { +export function getExceptionFromError(error: Error, options?: NodeOptions): SyncPromise { const name = error.name || error.constructor.name; const stack = extractStackFromError(error); - return new SyncPromise(resolve => + return new SyncPromise(resolve => parseStack(stack, options).then(frames => { const result = { stacktrace: { @@ -256,9 +256,9 @@ export function getExceptionFromError(error: Error, options?: NodeOptions): Sync /** * @hidden */ -export function parseError(error: ExtendedError, options?: NodeOptions): SyncPromise { - return new SyncPromise(resolve => - getExceptionFromError(error, options).then((exception: SentryException) => { +export function parseError(error: ExtendedError, options?: NodeOptions): SyncPromise { + return new SyncPromise(resolve => + getExceptionFromError(error, options).then((exception: Exception) => { resolve({ exception: { values: [exception], diff --git a/packages/node/src/transports/base.ts b/packages/node/src/transports/base.ts index 4bd74729b473..4e89dedaf8be 100644 --- a/packages/node/src/transports/base.ts +++ b/packages/node/src/transports/base.ts @@ -1,5 +1,5 @@ import { API } from '@sentry/core'; -import { SentryResponse, Status, Transport, TransportOptions } from '@sentry/types'; +import { Response, Status, Transport, TransportOptions } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import { PromiseBuffer } from '@sentry/utils/promisebuffer'; import * as fs from 'fs'; @@ -31,7 +31,7 @@ export abstract class BaseTransport implements Transport { public client?: http.Agent | https.Agent; /** A simple buffer holding all requests. */ - protected readonly buffer: PromiseBuffer = new PromiseBuffer(30); + protected readonly buffer: PromiseBuffer = new PromiseBuffer(30); /** Create instance and set this.dsn */ public constructor(public options: TransportOptions) { @@ -66,9 +66,9 @@ export abstract class BaseTransport implements Transport { } /** JSDoc */ - protected async sendWithModule(httpModule: HTTPRequest, body: string): Promise { + protected async sendWithModule(httpModule: HTTPRequest, body: string): Promise { return this.buffer.add( - new Promise((resolve, reject) => { + new Promise((resolve, reject) => { const req = httpModule.request(this.getRequestOptions(), (res: http.IncomingMessage) => { res.setEncoding('utf8'); if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { @@ -100,7 +100,7 @@ export abstract class BaseTransport implements Transport { /** * @inheritDoc */ - public async sendEvent(_: string): Promise { + public async sendEvent(_: string): Promise { throw new SentryError('Transport Class has to implement `sendEvent` method.'); } diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 4b1f557b6f14..dfd818b67750 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -1,4 +1,4 @@ -import { SentryResponse, TransportOptions } from '@sentry/types'; +import { Response, TransportOptions } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import * as http from 'http'; import * as HttpsProxyAgent from 'https-proxy-agent'; @@ -20,7 +20,7 @@ export class HTTPTransport extends BaseTransport { /** * @inheritDoc */ - public async sendEvent(body: string): Promise { + public async sendEvent(body: string): Promise { if (!this.module) { throw new SentryError('No module available in HTTPTransport'); } diff --git a/packages/node/src/transports/https.ts b/packages/node/src/transports/https.ts index 3117845b1ff1..41da385c0924 100644 --- a/packages/node/src/transports/https.ts +++ b/packages/node/src/transports/https.ts @@ -1,4 +1,4 @@ -import { SentryResponse, TransportOptions } from '@sentry/types'; +import { Response, TransportOptions } from '@sentry/types'; import { SentryError } from '@sentry/utils/error'; import * as https from 'https'; import * as HttpsProxyAgent from 'https-proxy-agent'; @@ -20,7 +20,7 @@ export class HTTPSTransport extends BaseTransport { /** * @inheritDoc */ - public async sendEvent(body: string): Promise { + public async sendEvent(body: string): Promise { if (!this.module) { throw new SentryError('No module available in HTTPSTransport'); } diff --git a/packages/node/test/helper/settimeouttransport.ts b/packages/node/test/helper/settimeouttransport.ts index 313504b2b083..2cfa2bd88465 100644 --- a/packages/node/test/helper/settimeouttransport.ts +++ b/packages/node/test/helper/settimeouttransport.ts @@ -1,10 +1,10 @@ -import { SentryResponse, Status } from '../../src'; +import { Response, Status } from '../../src'; import { BaseTransport } from '../../src/transports'; export class SetTimeoutTransport extends BaseTransport { - public async sendEvent(_: string): Promise { + public async sendEvent(_: string): Promise { return this.buffer.add( - new Promise(resolve => { + new Promise(resolve => { setTimeout(() => { resolve({ status: Status.fromHttpCode(200), diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 71b86f38c7bf..55d4f7e6f1f9 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,17 +1,16 @@ import * as domain from 'domain'; - import { addBreadcrumb, captureEvent, captureException, captureMessage, configureScope, + Event, getCurrentHub, init, NodeClient, + Response, Scope, - SentryEvent, - SentryResponse, } from '../src'; import { NodeBackend } from '../src/backend'; import { SetTimeoutTransport } from './helper/settimeouttransport'; @@ -110,7 +109,7 @@ describe('SentryNode', () => { }); describe('breadcrumbs', () => { - let s: jest.Mock<(event: SentryEvent) => Promise>; + let s: jest.Mock<(event: Event) => void>; beforeEach(() => { s = jest.spyOn(NodeBackend.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); @@ -122,7 +121,7 @@ describe('SentryNode', () => { test('record auto breadcrumbs', done => { const client = new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { // TODO: It should be 3, but we don't capture a breadcrumb // for our own captureMessage/captureException calls yet expect(event.breadcrumbs!).toHaveLength(2); @@ -139,7 +138,7 @@ describe('SentryNode', () => { }); describe('capture', () => { - let s: jest.Mock<(event: SentryEvent) => Promise>; + let s: jest.Mock<(event: Event) => void>; beforeEach(() => { s = jest.spyOn(NodeBackend.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); @@ -153,7 +152,7 @@ describe('SentryNode', () => { expect.assertions(5); getCurrentHub().bindClient( new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); expect(event.exception!.values![0]).not.toBeUndefined(); @@ -179,7 +178,7 @@ describe('SentryNode', () => { expect.assertions(10); getCurrentHub().bindClient( new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.tags).toEqual({ test: '1' }); expect(event.exception).not.toBeUndefined(); expect(event.exception!.values![0]).not.toBeUndefined(); @@ -211,7 +210,7 @@ describe('SentryNode', () => { expect.assertions(2); getCurrentHub().bindClient( new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.message).toBe('test'); expect(event.exception).toBeUndefined(); done(); @@ -227,7 +226,7 @@ describe('SentryNode', () => { expect.assertions(2); getCurrentHub().bindClient( new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.message).toBe('test event'); expect(event.exception).toBeUndefined(); done(); @@ -243,7 +242,7 @@ describe('SentryNode', () => { const d = domain.create(); const client = new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect(event.message).toBe('test domain'); expect(event.exception).toBeUndefined(); done(); @@ -263,7 +262,7 @@ describe('SentryNode', () => { expect.assertions(1); getCurrentHub().bindClient( new NodeClient({ - beforeSend: (event: SentryEvent) => { + beforeSend: (event: Event) => { expect( event.exception!.values![0].stacktrace!.frames![ event.exception!.values![0].stacktrace!.frames!.length - 1 diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 2ab688deeae0..97c1c28db899 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -1,4 +1,4 @@ -import { SentryEvent } from '../../src'; +import { Event } from '../../src'; import { NodeBackend } from '../../src/backend'; import { LinkedErrors } from '../../src/integrations/linkederrors'; @@ -31,7 +31,7 @@ describe('LinkedErrors', () => { const spy = jest.spyOn(linkedErrors, 'walkErrorTree'); const one = new Error('originalException'); const backend = new NodeBackend({}); - let event: SentryEvent | undefined; + let event: Event | undefined; return backend .eventFromException(one) .then(eventFromException => { @@ -146,7 +146,7 @@ describe('LinkedErrors', () => { originalException: one, }) .then(result => { - expect(result!.exception!.values.length).toEqual(2); + expect(result!.exception!.values!.length).toEqual(2); expect(result!.exception!.values![0].type).toEqual('TypeError'); expect(result!.exception!.values![0].value).toEqual('two'); expect(result!.exception!.values![0].stacktrace).toHaveProperty('frames'); diff --git a/packages/node/test/parsers.test.ts b/packages/node/test/parsers.test.ts index 01be51e7ea88..90372812f0d2 100644 --- a/packages/node/test/parsers.test.ts +++ b/packages/node/test/parsers.test.ts @@ -1,4 +1,3 @@ -// import { StackFrame } from '@sentry/types'; import * as fs from 'fs'; import * as stacktrace from 'stack-trace'; import * as Parsers from '../src/parsers'; diff --git a/packages/types/src/breadcrumb.ts b/packages/types/src/breadcrumb.ts new file mode 100644 index 000000000000..2f186ac7771c --- /dev/null +++ b/packages/types/src/breadcrumb.ts @@ -0,0 +1,17 @@ +import { Severity } from './severity'; + +/** JSDoc */ +export interface Breadcrumb { + type?: string; + level?: Severity; + event_id?: string; + category?: string; + message?: string; + data?: any; + timestamp?: number; +} + +/** JSDoc */ +export interface BreadcrumbHint { + [key: string]: any; +} diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts new file mode 100644 index 000000000000..c01f091c4e89 --- /dev/null +++ b/packages/types/src/client.ts @@ -0,0 +1,73 @@ +import { Dsn } from './dsn'; +import { Event, EventHint } from './event'; +import { Integration, IntegrationClass } from './integration'; +import { Options } from './options'; +import { Scope } from './scope'; +import { Severity } from './severity'; + +/** + * User-Facing Sentry SDK Client. + * + * This interface contains all methods to interface with the SDK once it has + * been installed. It allows to send events to Sentry, record breadcrumbs and + * set a context included in every event. Since the SDK mutates its environment, + * there will only be one instance during runtime. + * + */ +export interface Client { + /** + * Captures an exception event and sends it to Sentry. + * + * @param exception An exception-like object. + * @param hint May contain additional information about the original exception. + * @param scope An optional scope containing event metadata. + * @returns The event id + */ + captureException(exception: any, hint?: EventHint, scope?: Scope): string | undefined; + + /** + * Captures a message event and sends it to Sentry. + * + * @param message The message to send to Sentry. + * @param level Define the level of the message. + * @param hint May contain additional information about the original exception. + * @param scope An optional scope containing event metadata. + * @returns The event id + */ + captureMessage(message: string, level?: Severity, hint?: EventHint, scope?: Scope): string | undefined; + + /** + * Captures a manually created event and sends it to Sentry. + * + * @param event The event to send to Sentry. + * @param hint May contain additional information about the original exception. + * @param scope An optional scope containing event metadata. + * @returns The event id + */ + captureEvent(event: Event, hint?: EventHint, scope?: Scope): string | undefined; + + /** Returns the current Dsn. */ + getDsn(): Dsn | undefined; + + /** Returns the current options. */ + getOptions(): O; + + /** + * A promise that resolves when all current events have been sent. + * If you provide a timeout and the queue takes longer to drain the promise returns false. + * + * @param timeout Maximum time in ms the client should wait. + */ + close(timeout?: number): Promise; + + /** + * A promise that resolves when all current events have been sent. + * If you provide a timeout and the queue takes longer to drain the promise returns false. + * + * @param timeout Maximum time in ms the client should wait. + */ + flush(timeout?: number): Promise; + + /** Returns an array of installed integrations on the client. */ + getIntegration(integartion: IntegrationClass): T | null; +} diff --git a/packages/types/src/dsn.ts b/packages/types/src/dsn.ts new file mode 100644 index 000000000000..117b900064b2 --- /dev/null +++ b/packages/types/src/dsn.ts @@ -0,0 +1,37 @@ +/** Supported Sentry transport protocols in a Dsn. */ +export type DsnProtocol = 'http' | 'https'; + +/** Primitive components of a Dsn. */ +export interface DsnComponents { + /** Protocol used to connect to Sentry. */ + protocol: DsnProtocol; + /** Public authorization key. */ + user: string; + /** Private authorization key (deprecated, optional). */ + pass?: string; + /** Hostname of the Sentry instance. */ + host: string; + /** Port of the Sentry instance. */ + port?: string; + /** Sub path/ */ + path?: string; + /** Project ID */ + projectId: string; +} + +/** Anything that can be parsed into a Dsn. */ +export type DsnLike = string | DsnComponents; + +/** The Sentry Dsn, identifying a Sentry instance and project. */ +export interface Dsn { + /** + * Renders the string representation of this Dsn. + * + * By default, this will render the public representation without the password + * component. To get the deprecated private representation, set `withPassword` + * to true. + * + * @param withPassword When set to true, the password will be included. + */ + toString(withPassword: boolean): string; +} diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts new file mode 100644 index 000000000000..c8d4beed51a1 --- /dev/null +++ b/packages/types/src/event.ts @@ -0,0 +1,45 @@ +import { Breadcrumb } from './breadcrumb'; +import { Exception } from './exception'; +import { Mechanism } from './mechanism'; +import { Request } from './request'; +import { SdkInfo } from './sdkinfo'; +import { Severity } from './severity'; +import { Stacktrace } from './stacktrace'; +import { User } from './user'; + +/** JSDoc */ +export interface Event { + event_id?: string; + message?: string; + timestamp?: number; + level?: Severity; + platform?: string; + logger?: string; + server_name?: string; + release?: string; + dist?: string; + environment?: string; + sdk?: SdkInfo; + request?: Request; + transaction?: string; + modules?: { [key: string]: string }; + fingerprint?: string[]; + exception?: { + values?: Exception[]; + mechanism?: Mechanism; + }; + stacktrace?: Stacktrace; + breadcrumbs?: Breadcrumb[]; + contexts?: { [key: string]: object }; + tags?: { [key: string]: string }; + extra?: { [key: string]: any }; + user?: User; +} + +/** JSDoc */ +export interface EventHint { + event_id?: string; + syntheticException?: Error | null; + originalException?: Error | null; + data?: any; +} diff --git a/packages/types/src/eventprocessor.ts b/packages/types/src/eventprocessor.ts new file mode 100644 index 000000000000..07789fc40d76 --- /dev/null +++ b/packages/types/src/eventprocessor.ts @@ -0,0 +1,9 @@ +import { Event, EventHint } from './event'; + +/** + * Event processors are used to change the event before it will be send. + * We strongly advise to make this function sync. + * Returning a Promise will work just fine, but better be sure that you know what you are doing. + * Event processing will be deferred until your Promise is resolved. + */ +export type EventProcessor = (event: Event, hint?: EventHint) => Promise | Event | null; diff --git a/packages/types/src/exception.ts b/packages/types/src/exception.ts new file mode 100644 index 000000000000..8698da418557 --- /dev/null +++ b/packages/types/src/exception.ts @@ -0,0 +1,10 @@ +import { Stacktrace } from './stacktrace'; + +/** JSDoc */ +export interface Exception { + type?: string; + value?: string; + module?: string; + thread_id?: number; + stacktrace?: Stacktrace; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 33655e528980..99d49400fa0e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,324 +1,23 @@ -/** Supported Sentry transport protocols in a Dsn. */ -export type DsnProtocol = 'http' | 'https'; - -/** Primitive components of a Dsn. */ -export interface DsnComponents { - /** Protocol used to connect to Sentry. */ - protocol: DsnProtocol; - /** Public authorization key. */ - user: string; - /** Private authorization key (deprecated, optional). */ - pass?: string; - /** Hostname of the Sentry instance. */ - host: string; - /** Port of the Sentry instance. */ - port?: string; - /** Sub path/ */ - path?: string; - /** Project ID */ - projectId: string; -} - -/** Anything that can be parsed into a Dsn. */ -export type DsnLike = string | DsnComponents; - -/** JSDoc */ -export enum Severity { - /** JSDoc */ - Fatal = 'fatal', - /** JSDoc */ - Error = 'error', - /** JSDoc */ - Warning = 'warning', - /** JSDoc */ - Log = 'log', - /** JSDoc */ - Info = 'info', - /** JSDoc */ - Debug = 'debug', - /** JSDoc */ - Critical = 'critical', -} - -// tslint:disable:no-unnecessary-qualifier no-namespace -export namespace Severity { - /** - * Converts a string-based level into a {@link Severity}. - * - * @param level string representation of Severity - * @returns Severity - */ - export function fromString(level: string): Severity { - switch (level) { - case 'debug': - return Severity.Debug; - case 'info': - return Severity.Info; - case 'warn': - case 'warning': - return Severity.Warning; - case 'error': - return Severity.Error; - case 'fatal': - return Severity.Fatal; - case 'critical': - return Severity.Critical; - case 'log': - default: - return Severity.Log; - } - } -} - -/** JSDoc */ -export interface Breadcrumb { - type?: string; - level?: Severity; - event_id?: string; - category?: string; - message?: string; - data?: any; - timestamp?: number; -} - -/** JSDoc */ -export interface User { - [key: string]: any; - id?: string; - ip_address?: string; - email?: string; - username?: string; -} - -/** JSDoc */ -export interface SdkInfo { - name: string; - version: string; - integrations?: string[]; - packages?: Package[]; -} - -/** JSDoc */ -export interface Package { - name: string; - version: string; -} - -/** JSDoc */ -export interface StackFrame { - filename?: string; - function?: string; - module?: string; - platform?: string; - lineno?: number; - colno?: number; - abs_path?: string; - context_line?: string; - pre_context?: string[]; - post_context?: string[]; - in_app?: boolean; - vars?: { [key: string]: any }; -} - -/** JSDoc */ -export interface Stacktrace { - frames?: StackFrame[]; - frames_omitted?: [number, number]; -} - -/** JSDoc */ -export interface Thread { - id?: number; - name?: string; - stacktrace?: Stacktrace; - crashed?: boolean; - current?: boolean; -} - -/** JSDoc */ -export interface SentryException { - type?: string; - value?: string; - module?: string; - thread_id?: number; - stacktrace?: Stacktrace; -} - -/** JSDoc */ -export interface Request { - url?: string; - method?: string; - data?: any; - query_string?: string; - cookies?: { [key: string]: string }; - env?: { [key: string]: string }; - headers?: { [key: string]: string }; -} - -/** JSDoc */ -export interface SentryEvent { - event_id?: string; - message?: string; - timestamp?: number; - level?: Severity; - platform?: string; - logger?: string; - server_name?: string; - release?: string; - dist?: string; - environment?: string; - sdk?: SdkInfo; - request?: Request; - transaction?: string; - modules?: { [key: string]: string }; - fingerprint?: string[]; - exception?: { - values?: SentryException[]; - mechanism?: Mechanism; - }; - stacktrace?: Stacktrace; - breadcrumbs?: Breadcrumb[]; - contexts?: { [key: string]: object }; - tags?: { [key: string]: string }; - extra?: { [key: string]: any }; - user?: User; -} - -/** JSDoc */ -export interface Mechanism { - type: string; - handled: boolean; - data?: { - [key: string]: string; - }; -} - -/** Integration interface */ -export interface Integration { - /** - * Returns {@link IntegrationClass.id} - */ - name: string; - - // This takes no options on purpose, options should be passed in the constructor - setupOnce(): void; -} - -/** Integration Class Interface */ -export interface IntegrationClass { - new (): T; - /** - * Property that holds the integration name - */ - id: string; -} - -/** JSDoc */ -export interface SentryResponse { - status: Status; - event?: SentryEvent; - reason?: string; -} - -/** JSDoc */ -export interface TransportOptions { - [key: string]: any; - /** Sentry DSN */ - dsn: DsnLike; - /** Define custom headers */ - headers?: object; - /** Set a HTTP proxy that should be used for outbound requests. */ - httpProxy?: string; - /** Set a HTTPS proxy that should be used for outbound requests. */ - httpsProxy?: string; - /** HTTPS proxy certificates path */ - caCerts?: string; -} - -/** Transport used sending data to Sentry */ -export interface Transport { - /** - * Sends the body to the Store endpoint in Sentry. - * - * @param body String body that should be sent to Sentry. - */ - sendEvent(body: string): Promise; - - /** - * Call this function to wait until all pending requests have been sent. - * - * @param timeout Number time in ms to wait until the buffer is drained. - */ - close(timeout?: number): Promise; -} - -/** JSDoc */ -export interface TransportClass { - new (options: TransportOptions): T; -} - -/** The status of an event. */ -export enum Status { - /** The status could not be determined. */ - Unknown = 'unknown', - /** The event was skipped due to configuration or callbacks. */ - Skipped = 'skipped', - /** The event was sent to Sentry successfully. */ - Success = 'success', - /** The client is currently rate limited and will try again later. */ - RateLimit = 'rate_limit', - /** The event could not be processed. */ - Invalid = 'invalid', - /** A server-side error ocurred during submission. */ - Failed = 'failed', -} - -// tslint:disable:no-unnecessary-qualifier no-namespace -export namespace Status { - /** - * Converts a HTTP status code into a {@link Status}. - * - * @param code The HTTP response status code. - * @returns The send status or {@link Status.Unknown}. - */ - export function fromHttpCode(code: number): Status { - if (code >= 200 && code < 300) { - return Status.Success; - } - - if (code === 429) { - return Status.RateLimit; - } - - if (code >= 400 && code < 500) { - return Status.Invalid; - } - - if (code >= 500) { - return Status.Failed; - } - - return Status.Unknown; - } -} - -/** JSDoc */ -export interface SentryWrappedFunction extends Function { - [key: string]: any; - __sentry__?: boolean; - __sentry_wrapped__?: SentryWrappedFunction; - __sentry_original__?: SentryWrappedFunction; -} - -/** JSDoc */ -export interface SentryEventHint { - event_id?: string; - syntheticException?: Error | null; - originalException?: Error | null; - data?: any; -} - -/** JSDoc */ -export interface SentryBreadcrumbHint { - [key: string]: any; -} +export { Breadcrumb, BreadcrumbHint } from './breadcrumb'; +export { Client } from './client'; +export { Dsn, DsnComponents, DsnLike, DsnProtocol } from './dsn'; +export { Event, EventHint } from './event'; +export { EventProcessor } from './eventprocessor'; +export { Exception } from './exception'; +export { Integration, IntegrationClass } from './integration'; +export { LogLevel } from './loglevel'; +export { Mechanism } from './mechanism'; +export { Options } from './options'; +export { Package } from './package'; +export { Request } from './request'; +export { Response } from './response'; +export { Scope } from './scope'; +export { SdkInfo } from './sdkinfo'; +export { Severity } from './severity'; +export { StackFrame } from './stackframe'; +export { Stacktrace } from './stacktrace'; +export { Status } from './status'; +export { Thread } from './thread'; +export { Transport, TransportOptions, TransportClass } from './transport'; +export { User } from './user'; +export { WrappedFunction } from './wrappedfunction'; diff --git a/packages/types/src/integration.ts b/packages/types/src/integration.ts new file mode 100644 index 000000000000..953bc4aefa3b --- /dev/null +++ b/packages/types/src/integration.ts @@ -0,0 +1,19 @@ +/** Integration Class Interface */ +export interface IntegrationClass { + new (): T; + /** + * Property that holds the integration name + */ + id: string; +} + +/** Integration interface */ +export interface Integration { + /** + * Returns {@link IntegrationClass.id} + */ + name: string; + + // This takes no options on purpose, options should be passed in the constructor + setupOnce(): void; +} diff --git a/packages/types/src/loglevel.ts b/packages/types/src/loglevel.ts new file mode 100644 index 000000000000..7a75d69312dc --- /dev/null +++ b/packages/types/src/loglevel.ts @@ -0,0 +1,11 @@ +/** Console logging verbosity for the SDK. */ +export enum LogLevel { + /** No logs will be generated. */ + None = 0, + /** Only SDK internal errors will be logged. */ + Error = 1, + /** Information useful for debugging the SDK will be logged. */ + Debug = 2, + /** All SDK actions will be logged. */ + Verbose = 3, +} diff --git a/packages/types/src/mechanism.ts b/packages/types/src/mechanism.ts new file mode 100644 index 000000000000..34db72f46d65 --- /dev/null +++ b/packages/types/src/mechanism.ts @@ -0,0 +1,8 @@ +/** JSDoc */ +export interface Mechanism { + type: string; + handled: boolean; + data?: { + [key: string]: string; + }; +} diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts new file mode 100644 index 000000000000..56af609c37a6 --- /dev/null +++ b/packages/types/src/options.ts @@ -0,0 +1,107 @@ +import { Breadcrumb, BreadcrumbHint } from './breadcrumb'; +import { Event, EventHint } from './event'; +import { Integration } from './integration'; +import { LogLevel } from './loglevel'; +import { Transport, TransportClass, TransportOptions } from './transport'; + +/** Base configuration options for every SDK. */ +export interface Options { + /** + * Enable debug functionality in the SDK itself + */ + debug?: boolean; + + /** + * Specifies whether this SDK should activate and send events to Sentry. + * Disabling the SDK reduces all overhead from instrumentation, collecting + * breadcrumbs and capturing events. Defaults to true. + */ + enabled?: boolean; + + /** + * The Dsn used to connect to Sentry and identify the project. If omitted, the + * SDK will not send any data to Sentry. + */ + dsn?: string; + + /** + * If this is set to false, default integrations will not be added, otherwise this will internally be set to the + * recommended default integrations. + */ + defaultIntegrations?: false | Integration[]; + + /** + * List of integrations that should be installed after SDK was initialized. + * Accepts either a list of integrations or a function that receives + * default integrations and returns a new, updated list. + */ + integrations?: Integration[] | ((integrations: Integration[]) => Integration[]); + + /** + * A pattern for error messages which should not be sent to Sentry. + * By default, all errors will be sent. + */ + ignoreErrors?: Array; + + /** + * Transport object that should be used to send events to Sentry + */ + transport?: TransportClass; + + /** + * Options for the default transport that the SDK uses. + */ + transportOptions?: TransportOptions; + + /** + * The release identifier used when uploading respective source maps. Specify + * this value to allow Sentry to resolve the correct source maps when + * processing events. + */ + release?: string; + + /** The current environment of your application (e.g. "production"). */ + environment?: string; + + /** Sets the distribution for all events */ + dist?: string; + + /** The maximum number of breadcrumbs sent with events. Defaults to 100. */ + maxBreadcrumbs?: number; + + /** Console logging verbosity for the SDK Client. */ + logLevel?: LogLevel; + + /** A global sample rate to apply to all events (0 - 1). */ + sampleRate?: number; + + /** Attaches stacktraces to pure capture message / log integrations */ + attachStacktrace?: boolean; + + /** + * A callback invoked during event submission, allowing to optionally modify + * the event before it is sent to Sentry. + * + * Note that you must return a valid event from this callback. If you do not + * wish to modify the event, simply return it at the end. + * Returning null will case the event to be dropped. + * + * @param event The error or message event generated by the SDK. + * @param hint May contain additional information about the original exception. + * @returns A new event that will be sent | null. + */ + beforeSend?(event: Event, hint?: EventHint): Promise | Event | null; + + /** + * A callback invoked when adding a breadcrumb, allowing to optionally modify + * it before adding it to future events. + * + * Note that you must return a valid breadcrumb from this callback. If you do + * not wish to modify the breadcrumb, simply return it at the end. + * Returning null will case the breadcrumb to be dropped. + * + * @param breadcrumb The breadcrumb as created by the SDK. + * @returns The breadcrumb that will be added | null. + */ + beforeBreadcrumb?(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): Breadcrumb | null; +} diff --git a/packages/types/src/package.ts b/packages/types/src/package.ts new file mode 100644 index 000000000000..c44d66e62950 --- /dev/null +++ b/packages/types/src/package.ts @@ -0,0 +1,5 @@ +/** JSDoc */ +export interface Package { + name: string; + version: string; +} diff --git a/packages/types/src/request.ts b/packages/types/src/request.ts new file mode 100644 index 000000000000..980dd498aa73 --- /dev/null +++ b/packages/types/src/request.ts @@ -0,0 +1,10 @@ +/** JSDoc */ +export interface Request { + url?: string; + method?: string; + data?: any; + query_string?: string; + cookies?: { [key: string]: string }; + env?: { [key: string]: string }; + headers?: { [key: string]: string }; +} diff --git a/packages/types/src/response.ts b/packages/types/src/response.ts new file mode 100644 index 000000000000..44efc7f48c10 --- /dev/null +++ b/packages/types/src/response.ts @@ -0,0 +1,9 @@ +import { Event } from './event'; +import { Status } from './status'; + +/** JSDoc */ +export interface Response { + status: Status; + event?: Event; + reason?: string; +} diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts new file mode 100644 index 000000000000..a1aeadb7e7cb --- /dev/null +++ b/packages/types/src/scope.ts @@ -0,0 +1,53 @@ +import { Breadcrumb } from './breadcrumb'; +import { EventProcessor } from './eventprocessor'; +import { Severity } from './severity'; +import { User } from './user'; + +/** + * Holds additional event information. {@link Scope.applyToEvent} will be + * called by the client before an event will be sent. + */ +export interface Scope { + /** Add new event processor that will be called after {@link applyToEvent}. */ + addEventProcessor(callback: EventProcessor): Scope; + + /** + * Updates user context information for future events. + * @param user User context object to be set in the current context. + */ + setUser(user: User): Scope; + + /** + * Updates tags context information for future events. + * @param tags Tags context object to merge into current context. + */ + setTag(key: string, value: string): Scope; + + /** + * Updates extra context information for future events. + * @param extra context object to merge into current context. + */ + setExtra(key: string, extra: any): Scope; + + /** + * Sets the fingerprint on the scope to send with the events. + * @param fingerprint string[] to group events in Sentry. + */ + setFingerprint(fingerprint: string[]): Scope; + + /** + * Sets the level on the scope for future events. + * @param level string {@link Severity} + */ + setLevel(level: Severity): Scope; + + /** Clears the current scope and resets its properties. */ + clear(): void; + + /** + * Sets the breadcrumbs in the scope + * @param breadcrumbs Breadcrumb + * @param maxBreadcrumbs number of max breadcrumbs to merged into event. + */ + addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): void; +} diff --git a/packages/types/src/sdkinfo.ts b/packages/types/src/sdkinfo.ts new file mode 100644 index 000000000000..45500cad5482 --- /dev/null +++ b/packages/types/src/sdkinfo.ts @@ -0,0 +1,9 @@ +import { Package } from './package'; + +/** JSDoc */ +export interface SdkInfo { + name: string; + version: string; + integrations?: string[]; + packages?: Package[]; +} diff --git a/packages/types/src/severity.ts b/packages/types/src/severity.ts new file mode 100644 index 000000000000..51fcf1b32edb --- /dev/null +++ b/packages/types/src/severity.ts @@ -0,0 +1,47 @@ +/** JSDoc */ +export enum Severity { + /** JSDoc */ + Fatal = 'fatal', + /** JSDoc */ + Error = 'error', + /** JSDoc */ + Warning = 'warning', + /** JSDoc */ + Log = 'log', + /** JSDoc */ + Info = 'info', + /** JSDoc */ + Debug = 'debug', + /** JSDoc */ + Critical = 'critical', +} + +// tslint:disable:no-unnecessary-qualifier no-namespace +export namespace Severity { + /** + * Converts a string-based level into a {@link Severity}. + * + * @param level string representation of Severity + * @returns Severity + */ + export function fromString(level: string): Severity { + switch (level) { + case 'debug': + return Severity.Debug; + case 'info': + return Severity.Info; + case 'warn': + case 'warning': + return Severity.Warning; + case 'error': + return Severity.Error; + case 'fatal': + return Severity.Fatal; + case 'critical': + return Severity.Critical; + case 'log': + default: + return Severity.Log; + } + } +} diff --git a/packages/types/src/stackframe.ts b/packages/types/src/stackframe.ts new file mode 100644 index 000000000000..d228547c6c92 --- /dev/null +++ b/packages/types/src/stackframe.ts @@ -0,0 +1,15 @@ +/** JSDoc */ +export interface StackFrame { + filename?: string; + function?: string; + module?: string; + platform?: string; + lineno?: number; + colno?: number; + abs_path?: string; + context_line?: string; + pre_context?: string[]; + post_context?: string[]; + in_app?: boolean; + vars?: { [key: string]: any }; +} diff --git a/packages/types/src/stacktrace.ts b/packages/types/src/stacktrace.ts new file mode 100644 index 000000000000..120a1b471af6 --- /dev/null +++ b/packages/types/src/stacktrace.ts @@ -0,0 +1,7 @@ +import { StackFrame } from './stackframe'; + +/** JSDoc */ +export interface Stacktrace { + frames?: StackFrame[]; + frames_omitted?: [number, number]; +} diff --git a/packages/types/src/status.ts b/packages/types/src/status.ts new file mode 100644 index 000000000000..182efcacd20c --- /dev/null +++ b/packages/types/src/status.ts @@ -0,0 +1,44 @@ +/** The status of an event. */ +export enum Status { + /** The status could not be determined. */ + Unknown = 'unknown', + /** The event was skipped due to configuration or callbacks. */ + Skipped = 'skipped', + /** The event was sent to Sentry successfully. */ + Success = 'success', + /** The client is currently rate limited and will try again later. */ + RateLimit = 'rate_limit', + /** The event could not be processed. */ + Invalid = 'invalid', + /** A server-side error ocurred during submission. */ + Failed = 'failed', +} + +// tslint:disable:no-unnecessary-qualifier no-namespace +export namespace Status { + /** + * Converts a HTTP status code into a {@link Status}. + * + * @param code The HTTP response status code. + * @returns The send status or {@link Status.Unknown}. + */ + export function fromHttpCode(code: number): Status { + if (code >= 200 && code < 300) { + return Status.Success; + } + + if (code === 429) { + return Status.RateLimit; + } + + if (code >= 400 && code < 500) { + return Status.Invalid; + } + + if (code >= 500) { + return Status.Failed; + } + + return Status.Unknown; + } +} diff --git a/packages/types/src/thread.ts b/packages/types/src/thread.ts new file mode 100644 index 000000000000..5cc1a2b63891 --- /dev/null +++ b/packages/types/src/thread.ts @@ -0,0 +1,10 @@ +import { Stacktrace } from './stacktrace'; + +/** JSDoc */ +export interface Thread { + id?: number; + name?: string; + stacktrace?: Stacktrace; + crashed?: boolean; + current?: boolean; +} diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts new file mode 100644 index 000000000000..bbf1c480aa19 --- /dev/null +++ b/packages/types/src/transport.ts @@ -0,0 +1,39 @@ +import { DsnLike } from './dsn'; +import { Response } from './response'; + +/** Transport used sending data to Sentry */ +export interface Transport { + /** + * Sends the body to the Store endpoint in Sentry. + * + * @param body String body that should be sent to Sentry. + */ + sendEvent(body: string): Promise; + + /** + * Call this function to wait until all pending requests have been sent. + * + * @param timeout Number time in ms to wait until the buffer is drained. + */ + close(timeout?: number): Promise; +} + +/** JSDoc */ +export interface TransportClass { + new (options: TransportOptions): T; +} + +/** JSDoc */ +export interface TransportOptions { + [key: string]: any; + /** Sentry DSN */ + dsn: DsnLike; + /** Define custom headers */ + headers?: object; + /** Set a HTTP proxy that should be used for outbound requests. */ + httpProxy?: string; + /** Set a HTTPS proxy that should be used for outbound requests. */ + httpsProxy?: string; + /** HTTPS proxy certificates path */ + caCerts?: string; +} diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts new file mode 100644 index 000000000000..eae46b008b3e --- /dev/null +++ b/packages/types/src/user.ts @@ -0,0 +1,8 @@ +/** JSDoc */ +export interface User { + [key: string]: any; + id?: string; + ip_address?: string; + email?: string; + username?: string; +} diff --git a/packages/types/src/wrappedfunction.ts b/packages/types/src/wrappedfunction.ts new file mode 100644 index 000000000000..db48773efcca --- /dev/null +++ b/packages/types/src/wrappedfunction.ts @@ -0,0 +1,7 @@ +/** JSDoc */ +export interface WrappedFunction extends Function { + [key: string]: any; + __sentry__?: boolean; + __sentry_wrapped__?: WrappedFunction; + __sentry_original__?: WrappedFunction; +} diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 75c986a4f48e..b334ffe4633b 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -1,4 +1,4 @@ -import { SentryEvent, SentryWrappedFunction } from '@sentry/types'; +import { Event, WrappedFunction } from '@sentry/types'; import { isString } from './is'; /** @@ -206,7 +206,7 @@ export function parseUrl( * Extracts either message or type+value from an event that can be used for user-facing logs * @returns event's description */ -export function getEventDescription(event: SentryEvent): string { +export function getEventDescription(event: Event): string { if (event.message) { return event.message; } else if (event.exception && event.exception.values && event.exception.values[0]) { @@ -241,9 +241,9 @@ export function consoleSandbox(callback: () => any): any { // Restore all wrapped console methods levels.forEach(level => { - if (level in global.console && (originalConsole[level] as SentryWrappedFunction).__sentry__) { - wrappedLevels[level] = (originalConsole[level] as SentryWrappedFunction).__sentry_wrapped__; - originalConsole[level] = (originalConsole[level] as SentryWrappedFunction).__sentry_original__; + if (level in global.console && (originalConsole[level] as WrappedFunction).__sentry__) { + wrappedLevels[level] = (originalConsole[level] as WrappedFunction).__sentry_wrapped__; + originalConsole[level] = (originalConsole[level] as WrappedFunction).__sentry_original__; } }); diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index e45493ad5e75..1e286b63e941 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -1,4 +1,4 @@ -import { SentryWrappedFunction } from '@sentry/types'; +import { WrappedFunction } from '@sentry/types'; import { isArray, isNaN, isPlainObject, isPrimitive, isUndefined } from './is'; import { Memo } from './memo'; import { truncate } from './string'; @@ -57,11 +57,11 @@ export function clone(object: T): T { */ export function fill(source: { [key: string]: any }, name: string, replacement: (...args: any[]) => any): void { - if (!(name in source) || (source[name] as SentryWrappedFunction).__sentry__) { + if (!(name in source) || (source[name] as WrappedFunction).__sentry__) { return; } const original = source[name] as () => any; - const wrapped = replacement(original) as SentryWrappedFunction; + const wrapped = replacement(original) as WrappedFunction; // Make sure it's a function first, as we need to attach an empty prototype for `defineProperties` to work // otherwise it'll throw "TypeError: Object.defineProperties called on non-object"