Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions packages/sdk-core/src/BacktraceCoreClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CoreClientSetup } from './builder/CoreClientSetup.js';
import { Events } from './common/Events.js';
import { ReportEvents } from './events/ReportEvents.js';
import { ClientEvents } from './events/ClientEvents.js';
import {
BacktraceAttachment,
BacktraceAttributeProvider,
Expand Down Expand Up @@ -34,7 +34,9 @@ import { MetricsBuilder } from './modules/metrics/MetricsBuilder.js';
import { SingleSessionProvider } from './modules/metrics/SingleSessionProvider.js';
import { RateLimitWatcher } from './modules/rateLimiter/RateLimitWatcher.js';

export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = BacktraceConfiguration> {
export abstract class BacktraceCoreClient<
O extends BacktraceConfiguration = BacktraceConfiguration,
> extends Events<ClientEvents> {
/**
* Backtrace client instance
*/
Expand Down Expand Up @@ -112,7 +114,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
}

protected readonly options: O;
protected readonly reportEvents: Events<ReportEvents>;

protected readonly attributeManager: AttributeManager;
protected readonly attachmentManager: AttachmentManager;
protected readonly fileSystem?: FileSystem;
Expand All @@ -128,7 +130,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
private _enabled = false;

protected constructor(setup: CoreClientSetup<O>) {
this.reportEvents = new Events();
super();

this.options = setup.options;
this.fileSystem = setup.fileSystem;
Expand Down Expand Up @@ -316,7 +318,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
skipFrames: this.skipFrameOnMessage(data),
});

this.reportEvents.emit('before-skip', report);
this.emit('before-skip', report);

if (this.options.skipReport && this.options.skipReport(report)) {
return Promise.resolve(BacktraceReportSubmissionResult.ReportSkipped());
Expand All @@ -329,12 +331,12 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac

const submissionAttachments = this.generateSubmissionAttachments(report, reportAttachments);

this.reportEvents.emit('before-send', report, backtraceData, submissionAttachments);
this.emit('before-send', report, backtraceData, submissionAttachments);

return this._reportSubmission
.send(backtraceData, submissionAttachments, abortSignal)
.then((submissionResult) => {
this.reportEvents.emit('after-send', report, backtraceData, submissionAttachments, submissionResult);
this.emit('after-send', report, backtraceData, submissionAttachments, submissionResult);
return submissionResult;
});
}
Expand Down Expand Up @@ -403,7 +405,6 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
return {
client: this,
options: this.options,
reportEvents: this.reportEvents,
attributeManager: this.attributeManager,
attachmentManager: this.attachmentManager,
reportSubmission: this._reportSubmission,
Expand Down
26 changes: 12 additions & 14 deletions packages/sdk-core/src/common/Events.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
interface EventCallback {
callback: (...args: any[]) => unknown;
interface EventCallback<A extends any[] = any[]> {
callback: (...args: A) => unknown;
once?: boolean;
}

export class Events<
E extends Record<string | number | symbol, (...args: any[]) => unknown> = Record<
string | number | symbol,
(...args: any[]) => unknown
>,
> {
/* eslint-disable @typescript-eslint/no-explicit-any */
export type EventMap = Record<string, any[]>;

export class Events<E extends EventMap = EventMap> {
private readonly _callbacks: Partial<Record<keyof E, EventCallback[]>> = {};

public on<N extends keyof E>(event: N, callback: E[N]): this {
public on<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
this.addCallback(event, { callback });
return this;
}

public once<N extends keyof E>(event: N, callback: E[N]): this {
public once<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
this.addCallback(event, { callback, once: true });
return this;
}

public off<N extends keyof E>(event: N, callback: E[N]): this {
public off<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
this.removeCallback(event, callback);
return this;
}

public emit<N extends keyof E>(event: N, ...args: Parameters<E[N]>): boolean {
public emit<N extends keyof E>(event: N, ...args: E[N]): boolean {
const callbacks = this._callbacks[event];
if (!callbacks || !callbacks.length) {
return false;
Expand All @@ -48,7 +46,7 @@ export class Events<
return true;
}

private addCallback(event: keyof E, callback: EventCallback) {
private addCallback<A extends unknown[]>(event: keyof E, callback: EventCallback<A>) {
const list = this._callbacks[event];
if (list) {
list.push(callback);
Expand All @@ -57,7 +55,7 @@ export class Events<
}
}

private removeCallback(event: keyof E, callback: EventCallback['callback']) {
private removeCallback<A extends unknown[]>(event: keyof E, callback: EventCallback<A>['callback']) {
const list = this._callbacks[event];
if (!list) {
return;
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-core/src/events/AttachmentEvents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BacktraceAttachment } from '../model/attachment/index.js';

export type AttachmentEvents = {
'scoped-attachments-updated'(attachments: BacktraceAttachment[]): void;
'scoped-attachments-updated': [attachments: BacktraceAttachment[]];
};
2 changes: 1 addition & 1 deletion packages/sdk-core/src/events/AttributeEvents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReportData } from '../model/report/ReportData.js';

export type AttributeEvents = {
'scoped-attributes-updated'(attributes: ReportData): void;
'scoped-attributes-updated': [attributes: ReportData];
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { BacktraceData } from '../model/data/index.js';
import { BacktraceReportSubmissionResult, BacktraceSubmissionResponse } from '../model/http/index.js';
import { BacktraceReport } from '../model/report/BacktraceReport.js';

export type ReportEvents = {
'before-skip'(report: BacktraceReport): void;
'before-send'(report: BacktraceReport, data: BacktraceData, attachments: BacktraceAttachment[]): void;
'after-send'(
export type ClientEvents = {
'before-skip': [report: BacktraceReport];
'before-send': [report: BacktraceReport, data: BacktraceData, attachments: BacktraceAttachment[]];
'after-send': [
report: BacktraceReport,
data: BacktraceData,
attachments: BacktraceAttachment[],
result: BacktraceReportSubmissionResult<BacktraceSubmissionResponse>,
): void;
];
};
3 changes: 0 additions & 3 deletions packages/sdk-core/src/modules/BacktraceModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Events } from '../common/Events.js';
import { ReportEvents } from '../events/ReportEvents.js';
import {
BacktraceConfiguration,
BacktraceCoreClient,
Expand All @@ -17,7 +15,6 @@ export interface BacktraceModuleBindData {
readonly options: BacktraceConfiguration;
readonly attributeManager: AttributeManager;
readonly attachmentManager: AttachmentManager;
readonly reportEvents: Events<ReportEvents>;
readonly reportSubmission: BacktraceReportSubmission;
readonly requestHandler: BacktraceRequestHandler;
readonly database?: BacktraceDatabase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class BreadcrumbsManager implements BacktraceBreadcrumbs, BacktraceModule
}
}

public bind({ client, reportEvents, attachmentManager }: BacktraceModuleBindData): void {
public bind({ client, attachmentManager }: BacktraceModuleBindData): void {
if (this._storage.getAttachmentProviders) {
attachmentManager.addProviders(...this._storage.getAttachmentProviders());
} else {
Expand All @@ -77,7 +77,7 @@ export class BreadcrumbsManager implements BacktraceBreadcrumbs, BacktraceModule
[BREADCRUMB_ATTRIBUTE_NAME]: this._storage.lastBreadcrumbId,
}));

reportEvents.on('before-skip', (report) => this.logReport(report));
client.on('before-skip', (report) => this.logReport(report));
}

public initialize() {
Expand Down
37 changes: 33 additions & 4 deletions packages/sdk-core/src/modules/database/BacktraceDatabase.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { anySignal, createAbortController } from '../../common/AbortController.js';
import { Events } from '../../common/Events.js';
import { IdGenerator } from '../../common/IdGenerator.js';
import { TimeHelper } from '../../common/TimeHelper.js';
import { BacktraceAttachment } from '../../model/attachment/index.js';
import { BacktraceDatabaseConfiguration } from '../../model/configuration/BacktraceDatabaseConfiguration.js';
import { BacktraceData } from '../../model/data/BacktraceData.js';
import { BacktraceReportSubmission } from '../../model/http/BacktraceReportSubmission.js';
import { BacktraceReportSubmissionResult, BacktraceSubmissionResponse } from '../../model/http/index.js';
import { BacktraceModule, BacktraceModuleBindData } from '../BacktraceModule.js';
import { SessionFiles } from '../storage/index.js';
import { BacktraceDatabaseContext } from './BacktraceDatabaseContext.js';
Expand All @@ -16,7 +18,17 @@ import {
ReportBacktraceDatabaseRecord,
} from './model/BacktraceDatabaseRecord.js';

export class BacktraceDatabase implements BacktraceModule {
type BacktraceDatabaseEvents = {
added: [record: BacktraceDatabaseRecord];
removed: [record: BacktraceDatabaseRecord];
'before-send': [record: BacktraceDatabaseRecord];
'after-send': [
record: BacktraceDatabaseRecord,
result: BacktraceReportSubmissionResult<BacktraceSubmissionResponse>,
];
};

export class BacktraceDatabase extends Events<BacktraceDatabaseEvents> implements BacktraceModule {
/**
* Determines if the database is enabled.
*/
Expand Down Expand Up @@ -45,6 +57,8 @@ export class BacktraceDatabase implements BacktraceModule {
private readonly _requestHandler: BacktraceReportSubmission,
private readonly _sessionFiles?: SessionFiles,
) {
super();

this._databaseRecordContext = new BacktraceDatabaseContext(this._options?.maximumRetries);
this._recordLimits = {
report: this._options?.maximumNumberOfRecords ?? 8,
Expand Down Expand Up @@ -82,7 +96,7 @@ export class BacktraceDatabase implements BacktraceModule {
return true;
}

public bind({ reportEvents }: BacktraceModuleBindData): void {
public bind({ client }: BacktraceModuleBindData): void {
if (this._enabled) {
return;
}
Expand All @@ -91,7 +105,7 @@ export class BacktraceDatabase implements BacktraceModule {
return;
}

reportEvents.on('before-send', (_, data, attachments) => {
client.on('before-send', (_, data, attachments) => {
const record = this.add(data, attachments);

if (!record || record.locked) {
Expand All @@ -101,7 +115,7 @@ export class BacktraceDatabase implements BacktraceModule {
record.locked = true;
});

reportEvents.on('after-send', (_, data, __, submissionResult) => {
client.on('after-send', (_, data, __, submissionResult) => {
const record = this._databaseRecordContext.find(
(record) => record.type === 'report' && record.data.uuid === data.uuid,
);
Expand Down Expand Up @@ -151,6 +165,8 @@ export class BacktraceDatabase implements BacktraceModule {
this._databaseRecordContext.add(record);
this.lockSessionWithRecord(record);

this.emit('added', record);

return record;
}

Expand Down Expand Up @@ -189,6 +205,8 @@ export class BacktraceDatabase implements BacktraceModule {
this._databaseRecordContext.add(record);
this.lockSessionWithRecord(record);

this.emit('added', record);

return record;
}

Expand Down Expand Up @@ -238,6 +256,8 @@ export class BacktraceDatabase implements BacktraceModule {
this._databaseRecordContext.remove(record);
this._storageProvider.delete(record);
this._sessionFiles?.unlockPreviousSessions(record.id);

this.emit('removed', record);
}
}

Expand Down Expand Up @@ -287,11 +307,15 @@ export class BacktraceDatabase implements BacktraceModule {
try {
record.locked = true;

this.emit('before-send', record);

const result =
record.type === 'report'
? await this._requestHandler.send(record.data, record.attachments, signal)
: await this._requestHandler.sendAttachment(record.rxid, record.attachment, signal);

this.emit('after-send', record, result);

if (
result.status === 'Ok' ||
result.status === 'Unsupported' ||
Expand Down Expand Up @@ -356,10 +380,15 @@ export class BacktraceDatabase implements BacktraceModule {

for (const record of recordsToAdd) {
this.lockSessionWithRecord(record);
this.emit('added', record);
}
}

private async setupDatabaseAutoSend() {
if (!this._enabled) {
return;
}

if (this._options?.autoSend === false) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-core/src/modules/storage/SessionFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface FileSession {
}

type SessionEvents = {
unlocked(): void;
unlocked: [];
};

const SESSION_MARKER_PREFIX = 'bt-session';
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk-core/tests/mocks/testHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { BacktraceReportSubmissionResult, BacktraceRequestHandler } from '../../

export const testHttpClient: BacktraceRequestHandler = {
post: jest.fn().mockResolvedValue(Promise.resolve(BacktraceReportSubmissionResult.Ok('Ok'))),
postError: jest.fn().mockResolvedValue(Promise.resolve()),
postError: jest.fn().mockResolvedValue(Promise.resolve(BacktraceReportSubmissionResult.Ok('Ok'))),
};