|
1 |
| -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ |
2 |
| -// TODO: figure out member access types and remove the line above |
3 |
| - |
4 |
| -import type { ReplayRecordingData } from '@sentry/types'; |
5 | 1 | import { logger } from '@sentry/utils';
|
6 | 2 |
|
7 |
| -import type { AddEventResult, EventBuffer, RecordingEvent, WorkerRequest } from './types'; |
8 |
| -import workerString from './worker/worker.js'; |
9 |
| - |
10 |
| -interface CreateEventBufferParams { |
11 |
| - useCompression: boolean; |
12 |
| -} |
13 |
| - |
14 |
| -/** |
15 |
| - * Create an event buffer for replays. |
16 |
| - */ |
17 |
| -export function createEventBuffer({ useCompression }: CreateEventBufferParams): EventBuffer { |
18 |
| - // eslint-disable-next-line no-restricted-globals |
19 |
| - if (useCompression && window.Worker) { |
20 |
| - const workerBlob = new Blob([workerString]); |
21 |
| - const workerUrl = URL.createObjectURL(workerBlob); |
22 |
| - |
23 |
| - __DEBUG_BUILD__ && logger.log('[Replay] Using compression worker'); |
24 |
| - const worker = new Worker(workerUrl); |
25 |
| - return new EventBufferProxy(worker); |
26 |
| - } |
27 |
| - |
28 |
| - __DEBUG_BUILD__ && logger.log('[Replay] Using simple buffer'); |
29 |
| - return new EventBufferArray(); |
30 |
| -} |
31 |
| - |
32 |
| -/** |
33 |
| - * This proxy will try to use the compression worker, and fall back to use the simple buffer if an error occurs there. |
34 |
| - * This can happen e.g. if the worker cannot be loaded. |
35 |
| - * Exported only for testing. |
36 |
| - */ |
37 |
| -export class EventBufferProxy implements EventBuffer { |
38 |
| - private _fallback: EventBufferArray; |
39 |
| - private _compression: EventBufferCompressionWorker; |
40 |
| - private _used: EventBuffer; |
41 |
| - |
42 |
| - public constructor(worker: Worker) { |
43 |
| - this._fallback = new EventBufferArray(); |
44 |
| - this._compression = new EventBufferCompressionWorker(worker); |
45 |
| - this._used = this._fallback; |
46 |
| - |
47 |
| - void this._ensureWorkerIsLoaded(); |
48 |
| - } |
49 |
| - |
50 |
| - /** @inheritDoc */ |
51 |
| - public get pendingLength(): number { |
52 |
| - return this._used.pendingLength; |
53 |
| - } |
54 |
| - |
55 |
| - /** @inheritDoc */ |
56 |
| - public get pendingEvents(): RecordingEvent[] { |
57 |
| - return this._used.pendingEvents; |
58 |
| - } |
59 |
| - |
60 |
| - /** @inheritDoc */ |
61 |
| - public destroy(): void { |
62 |
| - this._fallback.destroy(); |
63 |
| - this._compression.destroy(); |
64 |
| - } |
65 |
| - |
66 |
| - /** |
67 |
| - * Add an event to the event buffer. |
68 |
| - * |
69 |
| - * Returns true if event was successfully added. |
70 |
| - */ |
71 |
| - public addEvent(event: RecordingEvent, isCheckout?: boolean): Promise<AddEventResult> { |
72 |
| - return this._used.addEvent(event, isCheckout); |
73 |
| - } |
74 |
| - |
75 |
| - /** @inheritDoc */ |
76 |
| - public finish(): Promise<ReplayRecordingData> { |
77 |
| - return this._used.finish(); |
78 |
| - } |
79 |
| - |
80 |
| - /** Ensure the worker has loaded. */ |
81 |
| - private async _ensureWorkerIsLoaded(): Promise<void> { |
82 |
| - try { |
83 |
| - await this._compression.ensureReady(); |
84 |
| - } catch (error) { |
85 |
| - // If the worker fails to load, we fall back to the simple buffer. |
86 |
| - // Nothing more to do from our side here |
87 |
| - __DEBUG_BUILD__ && logger.log('[Replay] Failed to load the compression worker, falling back to simple buffer'); |
88 |
| - return; |
89 |
| - } |
90 |
| - |
91 |
| - // Compression worker is ready, we can use it |
92 |
| - // Now we need to switch over the array buffer to the compression worker |
93 |
| - const addEventPromises: Promise<void>[] = []; |
94 |
| - for (const event of this._fallback.pendingEvents) { |
95 |
| - addEventPromises.push(this._compression.addEvent(event)); |
96 |
| - } |
97 |
| - |
98 |
| - // We switch over to the compression buffer immediately - any further events will be added |
99 |
| - // after the previously buffered ones |
100 |
| - this._used = this._compression; |
101 |
| - |
102 |
| - // Wait for original events to be re-added before resolving |
103 |
| - await Promise.all(addEventPromises); |
104 |
| - } |
105 |
| -} |
106 |
| - |
107 |
| -class EventBufferArray implements EventBuffer { |
108 |
| - private _events: RecordingEvent[]; |
109 |
| - |
110 |
| - public constructor() { |
111 |
| - this._events = []; |
112 |
| - } |
113 |
| - |
114 |
| - public get pendingLength(): number { |
115 |
| - return this._events.length; |
116 |
| - } |
117 |
| - |
118 |
| - /** |
119 |
| - * Returns the raw events that are buffered. In `EventBufferArray`, this is the |
120 |
| - * same as `this._events`. |
121 |
| - */ |
122 |
| - public get pendingEvents(): RecordingEvent[] { |
123 |
| - return this._events; |
124 |
| - } |
125 |
| - |
126 |
| - public destroy(): void { |
127 |
| - this._events = []; |
128 |
| - } |
129 |
| - |
130 |
| - public async addEvent(event: RecordingEvent, isCheckout?: boolean): Promise<AddEventResult> { |
131 |
| - if (isCheckout) { |
132 |
| - this._events = [event]; |
133 |
| - return; |
134 |
| - } |
135 |
| - |
136 |
| - this._events.push(event); |
137 |
| - return; |
138 |
| - } |
139 |
| - |
140 |
| - public finish(): Promise<string> { |
141 |
| - return new Promise<string>(resolve => { |
142 |
| - // Make a copy of the events array reference and immediately clear the |
143 |
| - // events member so that we do not lose new events while uploading |
144 |
| - // attachment. |
145 |
| - const eventsRet = this._events; |
146 |
| - this._events = []; |
147 |
| - resolve(JSON.stringify(eventsRet)); |
148 |
| - }); |
149 |
| - } |
150 |
| -} |
| 3 | +import type { AddEventResult, EventBuffer, RecordingEvent, WorkerRequest, WorkerResponse } from '../types'; |
151 | 4 |
|
152 | 5 | /**
|
153 | 6 | * Event buffer that uses a web worker to compress events.
|
@@ -194,7 +47,7 @@ export class EventBufferCompressionWorker implements EventBuffer {
|
194 | 47 | this._worker.addEventListener(
|
195 | 48 | 'message',
|
196 | 49 | ({ data }: MessageEvent) => {
|
197 |
| - if (data.success) { |
| 50 | + if ((data as WorkerResponse).success) { |
198 | 51 | resolve();
|
199 | 52 | } else {
|
200 | 53 | reject();
|
@@ -257,30 +110,30 @@ export class EventBufferCompressionWorker implements EventBuffer {
|
257 | 110 | */
|
258 | 111 | private _postMessage<T>({ id, method, args }: WorkerRequest): Promise<T> {
|
259 | 112 | return new Promise((resolve, reject) => {
|
260 |
| - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type |
261 |
| - const listener = ({ data }: MessageEvent) => { |
262 |
| - if (data.method !== method) { |
| 113 | + const listener = ({ data }: MessageEvent): void => { |
| 114 | + const response = data as WorkerResponse; |
| 115 | + if (response.method !== method) { |
263 | 116 | return;
|
264 | 117 | }
|
265 | 118 |
|
266 | 119 | // There can be multiple listeners for a single method, the id ensures
|
267 | 120 | // that the response matches the caller.
|
268 |
| - if (data.id !== id) { |
| 121 | + if (response.id !== id) { |
269 | 122 | return;
|
270 | 123 | }
|
271 | 124 |
|
272 | 125 | // At this point, we'll always want to remove listener regardless of result status
|
273 | 126 | this._worker.removeEventListener('message', listener);
|
274 | 127 |
|
275 |
| - if (!data.success) { |
| 128 | + if (!response.success) { |
276 | 129 | // TODO: Do some error handling, not sure what
|
277 |
| - __DEBUG_BUILD__ && logger.error('[Replay]', data.response); |
| 130 | + __DEBUG_BUILD__ && logger.error('[Replay]', response.response); |
278 | 131 |
|
279 | 132 | reject(new Error('Error in compression worker'));
|
280 | 133 | return;
|
281 | 134 | }
|
282 | 135 |
|
283 |
| - resolve(data.response); |
| 136 | + resolve(response.response as T); |
284 | 137 | };
|
285 | 138 |
|
286 | 139 | let stringifiedArgs;
|
|
0 commit comments