Skip to content

ref(replay): Add jsdoc to all replay modules #6654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions docs/event-sending.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ This document gives an outline for how event sending works, and which which plac
## Replay (WIP)

* `replay.sendReplayRequest()`
* `createPayload()`
* `getReplayEvent()`
* `createRecordingData()`
* `prepareReplayEvent()`
* `client._prepareEvent()` (see baseclient)
* `baseclient._applyClientOptions()`
* `baseclient._applyIntegrationsMetadata()`
Expand Down
4 changes: 1 addition & 3 deletions packages/replay/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ module.exports = {
rules: {
// TODO (high-prio): Re-enable this after migration
'@typescript-eslint/explicit-member-accessibility': 'off',
// TODO (high-prio): Re-enable this after migration
// Since we target only es6 here, we can leave this off
'@sentry-internal/sdk/no-async-await': 'off',
Comment on lines +28 to 29
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!

// TODO (medium-prio): Re-enable this after migration
'jsdoc/require-jsdoc': 'off',
},
},
{
Expand Down
3 changes: 3 additions & 0 deletions packages/replay/src/coreHandlers/breadcrumbHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import type { InstrumentationTypeBreadcrumb } from '../types';
import { DomHandlerData, handleDom } from './handleDom';
import { handleScope } from './handleScope';

/**
* An event handler to react to breadcrumbs.
*/
export function breadcrumbHandler(type: InstrumentationTypeBreadcrumb, handlerData: unknown): Breadcrumb | null {
if (type === 'scope') {
return handleScope(handlerData as Scope);
Expand Down
3 changes: 3 additions & 0 deletions packages/replay/src/coreHandlers/handleDom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export interface DomHandlerData {
event: Node | { target: Node };
}

/**
* An event handler to react to DOM events.
*/
export function handleDom(handlerData: DomHandlerData): Breadcrumb | null {
// Taken from https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/integrations/breadcrumbs.ts#L112
let target;
Expand Down
3 changes: 1 addition & 2 deletions packages/replay/src/coreHandlers/handleFetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ReplayPerformanceEntry } from '../createPerformanceEntry';
import type { ReplayContainer } from '../types';
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
import { createPerformanceSpans } from '../util/createPerformanceSpans';
import { shouldFilterRequest } from '../util/shouldFilterRequest';

Expand Down
3 changes: 1 addition & 2 deletions packages/replay/src/coreHandlers/handleHistory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ReplayPerformanceEntry } from '../createPerformanceEntry';
import type { ReplayContainer } from '../types';
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
import { createPerformanceSpans } from '../util/createPerformanceSpans';

interface HistoryHandlerData {
Expand Down
3 changes: 3 additions & 0 deletions packages/replay/src/coreHandlers/handleScope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { createBreadcrumb } from '../util/createBreadcrumb';

let _LAST_BREADCRUMB: null | Breadcrumb = null;

/**
* An event handler to handle scope changes.
*/
export function handleScope(scope: Scope): Breadcrumb | null {
const newBreadcrumb = scope.getLastBreadcrumb();

Expand Down
3 changes: 1 addition & 2 deletions packages/replay/src/coreHandlers/handleXhr.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ReplayPerformanceEntry } from '../createPerformanceEntry';
import type { ReplayContainer } from '../types';
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
import { createPerformanceSpans } from '../util/createPerformanceSpans';
import { shouldFilterRequest } from '../util/shouldFilterRequest';

Expand Down
85 changes: 16 additions & 69 deletions packages/replay/src/createPerformanceEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,12 @@ import { browserPerformanceTimeOrigin } from '@sentry/utils';
import { record } from 'rrweb';

import { WINDOW } from './constants';
import type { AllPerformanceEntry, PerformanceNavigationTiming, PerformancePaintTiming } from './types';

export interface ReplayPerformanceEntry {
/**
* One of these types https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
*/
type: string;

/**
* A more specific description of the performance entry
*/
name: string;

/**
* The start timestamp in seconds
*/
start: number;

/**
* The end timestamp in seconds
*/
end: number;

/**
* Additional unstructured data to be included
*/
data?: Record<string, unknown>;
}

interface MemoryInfo {
jsHeapSizeLimit: number;
totalJSHeapSize: number;
usedJSHeapSize: number;
}
import type {
AllPerformanceEntry,
PerformanceNavigationTiming,
PerformancePaintTiming,
ReplayPerformanceEntry,
} from './types';

// Map entryType -> function to normalize data for event
// @ts-ignore TODO: entry type does not fit the create* functions entry type
Expand All @@ -46,9 +18,12 @@ const ENTRY_TYPES: Record<string, (entry: AllPerformanceEntry) => null | ReplayP
// @ts-ignore TODO: entry type does not fit the create* functions entry type
navigation: createNavigationEntry,
// @ts-ignore TODO: entry type does not fit the create* functions entry type
['largest-contentful-paint']: createLargestContentfulPaint,
'largest-contentful-paint': createLargestContentfulPaint,
};

/**
* Create replay performance entries from the browser performance entries.
*/
export function createPerformanceEntries(entries: AllPerformanceEntry[]): ReplayPerformanceEntry[] {
return entries.map(createPerformanceEntry).filter(Boolean) as ReplayPerformanceEntry[];
}
Expand All @@ -67,9 +42,7 @@ function getAbsoluteTime(time: number): number {
return ((browserPerformanceTimeOrigin || WINDOW.performance.timeOrigin) + time) / 1000;
}

// TODO: type definition!
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createPaintEntry(entry: PerformancePaintTiming) {
function createPaintEntry(entry: PerformancePaintTiming): ReplayPerformanceEntry {
const { duration, entryType, name, startTime } = entry;

const start = getAbsoluteTime(startTime);
Expand All @@ -81,9 +54,7 @@ function createPaintEntry(entry: PerformancePaintTiming) {
};
}

// TODO: type definition!
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createNavigationEntry(entry: PerformanceNavigationTiming) {
function createNavigationEntry(entry: PerformanceNavigationTiming): ReplayPerformanceEntry | null {
// TODO: There looks to be some more interesting bits in here (domComplete, domContentLoaded)
const { entryType, name, duration, domComplete, startTime, transferSize, type } = entry;

Expand All @@ -104,9 +75,7 @@ function createNavigationEntry(entry: PerformanceNavigationTiming) {
};
}

// TODO: type definition!
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createResourceEntry(entry: PerformanceResourceTiming) {
function createResourceEntry(entry: PerformanceResourceTiming): ReplayPerformanceEntry | null {
const { entryType, initiatorType, name, responseEnd, startTime, encodedBodySize, transferSize } = entry;

// Core SDK handles these
Expand All @@ -126,9 +95,9 @@ function createResourceEntry(entry: PerformanceResourceTiming) {
};
}

// TODO: type definition!
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createLargestContentfulPaint(entry: PerformanceEntry & { size: number; element: Node }) {
function createLargestContentfulPaint(
entry: PerformanceEntry & { size: number; element: Node },
): ReplayPerformanceEntry {
const { duration, entryType, startTime, size } = entry;

const start = getAbsoluteTime(startTime);
Expand All @@ -147,25 +116,3 @@ function createLargestContentfulPaint(entry: PerformanceEntry & { size: number;
},
};
}

type ReplayMemoryEntry = ReplayPerformanceEntry & { data: { memory: MemoryInfo } };

export function createMemoryEntry(memoryEntry: MemoryInfo): ReplayMemoryEntry {
const { jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize } = memoryEntry;
// we don't want to use `getAbsoluteTime` because it adds the event time to the
// time origin, so we get the current timestamp instead
const time = new Date().getTime() / 1000;
return {
type: 'memory',
name: 'memory',
start: time,
end: time,
data: {
memory: {
jsHeapSizeLimit,
totalJSHeapSize,
usedJSHeapSize,
},
},
};
}
24 changes: 23 additions & 1 deletion packages/replay/src/eventBuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ interface CreateEventBufferParams {
useCompression: boolean;
}

/**
* Create an event buffer for replays.
*/
export function createEventBuffer({ useCompression }: CreateEventBufferParams): EventBuffer {
// eslint-disable-next-line no-restricted-globals
if (useCompression && window.Worker) {
Expand Down Expand Up @@ -72,7 +75,10 @@ class EventBufferArray implements EventBuffer {
}
}

// exporting for testing
/**
* Event buffer that uses a web worker to compress events.
* Exported only for testing.
*/
export class EventBufferCompressionWorker implements EventBuffer {
private _worker: null | Worker;
private _eventBufferItemLength: number = 0;
Expand All @@ -90,12 +96,18 @@ export class EventBufferCompressionWorker implements EventBuffer {
return this._eventBufferItemLength;
}

/**
* Destroy the event buffer.
*/
public destroy(): void {
__DEBUG_BUILD__ && logger.log('[Replay] Destroying compression worker');
this._worker?.terminate();
this._worker = null;
}

/**
* Add an event to the event buffer.
*/
public async addEvent(event: RecordingEvent, isCheckout?: boolean): Promise<ReplayRecordingData> {
if (isCheckout) {
// This event is a checkout, make sure worker buffer is cleared before
Expand All @@ -110,6 +122,9 @@ export class EventBufferCompressionWorker implements EventBuffer {
return this._sendEventToWorker(event);
}

/**
* Finish the event buffer and return the compressed data.
*/
public finish(): Promise<Uint8Array> {
return this._finishRequest(this._getAndIncrementId());
}
Expand Down Expand Up @@ -160,6 +175,9 @@ export class EventBufferCompressionWorker implements EventBuffer {
});
}

/**
* Send the event to the worker.
*/
private _sendEventToWorker(event: RecordingEvent): Promise<ReplayRecordingData> {
const promise = this._postMessage({
id: this._getAndIncrementId(),
Expand All @@ -173,6 +191,9 @@ export class EventBufferCompressionWorker implements EventBuffer {
return promise;
}

/**
* Finish the request and return the compressed data from the worker.
*/
private async _finishRequest(id: number): Promise<Uint8Array> {
const promise = this._postMessage({ id, method: 'finish', args: [] });

Expand All @@ -182,6 +203,7 @@ export class EventBufferCompressionWorker implements EventBuffer {
return promise as Promise<Uint8Array>;
}

/** Get the current ID and increment it for the next call. */
private _getAndIncrementId(): number {
return this._id++;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const MEDIA_SELECTORS = 'img,image,svg,path,rect,area,video,object,picture,embed

let _initialized = false;

/**
* The main replay integration class, to be passed to `init({ integrations: [] })`.
*/
export class Replay implements Integration {
/**
* @inheritDoc
Expand Down Expand Up @@ -126,10 +129,12 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
this._isInitialized = true;
}

/** If replay has already been initialized */
protected get _isInitialized(): boolean {
return _initialized;
}

/** Update _isInitialized */
protected set _isInitialized(value: boolean) {
_initialized = value;
}
Expand Down Expand Up @@ -181,6 +186,7 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
this._replay.stop();
}

/** Setup the integration. */
private _setup(): void {
// Client is not available in constructor, so we need to wait until setupOnce
this._loadReplayOptionsFromClient();
Expand Down
16 changes: 11 additions & 5 deletions packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ import type {
import { addEvent } from './util/addEvent';
import { addMemoryEntry } from './util/addMemoryEntry';
import { createBreadcrumb } from './util/createBreadcrumb';
import { createPayload } from './util/createPayload';
import { createPerformanceSpans } from './util/createPerformanceSpans';
import { createRecordingData } from './util/createRecordingData';
import { createReplayEnvelope } from './util/createReplayEnvelope';
import { debounce } from './util/debounce';
import { getReplayEvent } from './util/getReplayEvent';
import { isExpired } from './util/isExpired';
import { isSessionExpired } from './util/isSessionExpired';
import { overwriteRecordDroppedEvent, restoreRecordDroppedEvent } from './util/monkeyPatchRecordDroppedEvent';
import { prepareReplayEvent } from './util/prepareReplayEvent';

/**
* Returns true to return control to calling function, otherwise continue with normal batching
Expand All @@ -56,6 +56,9 @@ import { overwriteRecordDroppedEvent, restoreRecordDroppedEvent } from './util/m
const BASE_RETRY_INTERVAL = 5000;
const MAX_RETRY_COUNT = 3;

/**
* The main replay container class, which holds all the state and methods for recording and sending replays.
*/
export class ReplayContainer implements ReplayContainerInterface {
public eventBuffer: EventBuffer | null = null;

Expand Down Expand Up @@ -904,7 +907,7 @@ export class ReplayContainer implements ReplayContainerInterface {
includeReplayStartTimestamp,
eventContext,
}: SendReplay): Promise<void | TransportMakeRequestResponse> {
const payloadWithSequence = createPayload({
const recordingData = createRecordingData({
events,
headers: {
segment_id,
Expand Down Expand Up @@ -938,7 +941,7 @@ export class ReplayContainer implements ReplayContainerInterface {
replay_type: this.session.sampled,
};

const replayEvent = await getReplayEvent({ scope, client, replayId, event: baseEvent });
const replayEvent = await prepareReplayEvent({ scope, client, replayId, event: baseEvent });

if (!replayEvent) {
// Taken from baseclient's `_processEvent` method, where this is handled for errors/transactions
Expand Down Expand Up @@ -989,7 +992,7 @@ export class ReplayContainer implements ReplayContainerInterface {
}
*/

const envelope = createReplayEnvelope(replayEvent, payloadWithSequence, dsn, client.getOptions().tunnel);
const envelope = createReplayEnvelope(replayEvent, recordingData, dsn, client.getOptions().tunnel);

try {
return await transport.send(envelope);
Expand All @@ -998,6 +1001,9 @@ export class ReplayContainer implements ReplayContainerInterface {
}
}

/**
* Reset the counter of retries for sending replays.
*/
resetRetries(): void {
this._retryCount = 0;
this._retryInterval = BASE_RETRY_INTERVAL;
Expand Down
3 changes: 3 additions & 0 deletions packages/replay/src/session/saveSession.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { REPLAY_SESSION_KEY, WINDOW } from '../constants';
import type { Session } from '../types';

/**
* Save a session to session storage.
*/
export function saveSession(session: Session): void {
const hasSessionStorage = 'sessionStorage' in WINDOW;
if (!hasSessionStorage) {
Expand Down
Loading