Skip to content
Closed
Changes from all 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
26 changes: 14 additions & 12 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ function setupWeightBasedFlushing<
});
}

class HookEvent extends Event {
hookData: unknown[];
constructor(hook: string, ...rest: unknown[]) {
super(hook);
this.hookData = rest;
}
}

/**
* Base implementation for all JavaScript SDK clients.
*
Expand Down Expand Up @@ -200,8 +208,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
/** Holds flushable */
private _outcomes: { [key: string]: number };

// eslint-disable-next-line @typescript-eslint/ban-types
private _hooks: Record<string, Set<Function>>;
private _hooksTarget: EventTarget;

private _promiseBuffer: PromiseBuffer<unknown>;

Expand All @@ -215,7 +222,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
this._integrations = {};
this._numProcessing = 0;
this._outcomes = {};
this._hooks = {};
this._hooksTarget = new EventTarget();
this._eventProcessors = [];
this._promiseBuffer = makePromiseBuffer(options.transportOptions?.bufferSize ?? DEFAULT_TRANSPORT_BUFFER_SIZE);

Expand Down Expand Up @@ -840,23 +847,21 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
* Register a hook on this client.
*/
public on(hook: string, callback: unknown): () => void {
const hookCallbacks = (this._hooks[hook] = this._hooks[hook] || new Set());

// Wrap the callback in a function so that registering the same callback instance multiple
// times results in the callback being called multiple times.
// @ts-expect-error - The `callback` type is correct and must be a function due to the
// individual, specific overloads of this function.
// eslint-disable-next-line @typescript-eslint/ban-types
const uniqueCallback: Function = (...args: unknown[]) => callback(...args);
const uniqueCallback = (event: HookEvent) => callback(event.hookData);

hookCallbacks.add(uniqueCallback);
this._hooksTarget.addEventListener(hook, uniqueCallback as EventListener);

// This function returns a callback execution handler that, when invoked,
// deregisters a callback. This is crucial for managing instances where callbacks
// need to be unregistered to prevent self-referencing in callback closures,
// ensuring proper garbage collection.
return () => {
hookCallbacks.delete(uniqueCallback);
this._hooksTarget.removeEventListener(hook, uniqueCallback as EventListener);
};
}

Expand Down Expand Up @@ -1066,10 +1071,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
* Emit a hook that was previously registered via `on()`.
*/
public emit(hook: string, ...rest: unknown[]): void {
const callbacks = this._hooks[hook];
if (callbacks) {
callbacks.forEach(callback => callback(...rest));
}
this._hooksTarget.dispatchEvent(new HookEvent(hook, ...rest));
}

/**
Expand Down
Loading