@neroom/nevision 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +63 -56
- package/dist/index.mjs +63 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
38
|
var import_react = require("react");
|
|
39
39
|
var DEFAULT_API_URL = "https://api.ne-room.io";
|
|
40
|
-
var CHUNK_INTERVAL =
|
|
40
|
+
var CHUNK_INTERVAL = 2e3;
|
|
41
41
|
var MAX_EVENTS_PER_CHUNK = 100;
|
|
42
42
|
var DB_NAME = "nevision_recordings";
|
|
43
43
|
var STORE_NAME = "pending_events";
|
|
@@ -173,7 +173,6 @@ function NevisionRecorder({
|
|
|
173
173
|
const recordConfig = {
|
|
174
174
|
emit: (event) => {
|
|
175
175
|
eventsBufferRef.current.push(event);
|
|
176
|
-
persistEventsToStorage();
|
|
177
176
|
},
|
|
178
177
|
sampling: currentSampling ? {
|
|
179
178
|
mousemove: currentSampling.mousemove,
|
|
@@ -197,6 +196,36 @@ function NevisionRecorder({
|
|
|
197
196
|
if (stopFn) {
|
|
198
197
|
stopFnRef.current = stopFn;
|
|
199
198
|
}
|
|
199
|
+
const emitPageLifecycleEvent = (eventName) => {
|
|
200
|
+
const customEvent = {
|
|
201
|
+
type: 5,
|
|
202
|
+
// Custom event type in rrweb
|
|
203
|
+
data: {
|
|
204
|
+
tag: "page_lifecycle",
|
|
205
|
+
payload: {
|
|
206
|
+
event: eventName
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
timestamp: Date.now()
|
|
210
|
+
};
|
|
211
|
+
eventsBufferRef.current.push(customEvent);
|
|
212
|
+
};
|
|
213
|
+
const handleVisibilityChangeForTimeline = () => {
|
|
214
|
+
if (document.visibilityState === "hidden") {
|
|
215
|
+
emitPageLifecycleEvent("page_hidden");
|
|
216
|
+
} else {
|
|
217
|
+
emitPageLifecycleEvent("page_visible");
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const handleWindowFocusForTimeline = () => {
|
|
221
|
+
emitPageLifecycleEvent("page_focus");
|
|
222
|
+
};
|
|
223
|
+
const handleWindowBlurForTimeline = () => {
|
|
224
|
+
emitPageLifecycleEvent("page_blur");
|
|
225
|
+
};
|
|
226
|
+
document.addEventListener("visibilitychange", handleVisibilityChangeForTimeline);
|
|
227
|
+
window.addEventListener("focus", handleWindowFocusForTimeline);
|
|
228
|
+
window.addEventListener("blur", handleWindowBlurForTimeline);
|
|
200
229
|
intervalId = setInterval(() => {
|
|
201
230
|
sendChunk(apiUrl, sessionId);
|
|
202
231
|
}, CHUNK_INTERVAL);
|
|
@@ -211,44 +240,30 @@ function NevisionRecorder({
|
|
|
211
240
|
endSession(apiUrl, sessionIdRef.current);
|
|
212
241
|
}
|
|
213
242
|
};
|
|
214
|
-
|
|
243
|
+
const handleVisibilityChange = () => {
|
|
215
244
|
if (document.visibilityState === "hidden") {
|
|
216
245
|
handleFinalSync(false);
|
|
217
246
|
}
|
|
218
|
-
}
|
|
219
|
-
|
|
247
|
+
};
|
|
248
|
+
const handleBeforeUnload = () => {
|
|
220
249
|
handleFinalSync(true);
|
|
221
|
-
}
|
|
222
|
-
|
|
250
|
+
};
|
|
251
|
+
const handlePageHide = (e) => {
|
|
223
252
|
handleFinalSync(!e.persisted);
|
|
224
|
-
}
|
|
253
|
+
};
|
|
254
|
+
const handleWindowBlur = () => {
|
|
255
|
+
if (eventsBufferRef.current.length > 0 && sessionIdRef.current) {
|
|
256
|
+
sendChunk(apiUrl, sessionIdRef.current, true);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
260
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
261
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
262
|
+
window.addEventListener("blur", handleWindowBlur);
|
|
225
263
|
} catch (error) {
|
|
226
264
|
onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
227
265
|
}
|
|
228
266
|
};
|
|
229
|
-
let persistTimeout = null;
|
|
230
|
-
const persistEventsToStorage = () => {
|
|
231
|
-
if (persistTimeout) return;
|
|
232
|
-
persistTimeout = setTimeout(async () => {
|
|
233
|
-
persistTimeout = null;
|
|
234
|
-
if (!sessionIdRef.current || !eventStoreRef.current) return;
|
|
235
|
-
if (eventsBufferRef.current.length === 0) return;
|
|
236
|
-
const events = [...eventsBufferRef.current];
|
|
237
|
-
const chunkId = `${sessionIdRef.current}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
238
|
-
try {
|
|
239
|
-
await eventStoreRef.current.saveChunk({
|
|
240
|
-
id: chunkId,
|
|
241
|
-
sessionId: sessionIdRef.current,
|
|
242
|
-
siteId,
|
|
243
|
-
apiKey,
|
|
244
|
-
events,
|
|
245
|
-
chunkIndex: chunkIndexRef.current,
|
|
246
|
-
timestamp: Date.now()
|
|
247
|
-
});
|
|
248
|
-
} catch {
|
|
249
|
-
}
|
|
250
|
-
}, 1e3);
|
|
251
|
-
};
|
|
252
267
|
const retrySendPendingChunks = async (url) => {
|
|
253
268
|
if (!eventStoreRef.current) return;
|
|
254
269
|
try {
|
|
@@ -277,23 +292,8 @@ function NevisionRecorder({
|
|
|
277
292
|
};
|
|
278
293
|
const sendChunk = async (url, sessionId, isBeacon = false) => {
|
|
279
294
|
if (eventsBufferRef.current.length === 0) return;
|
|
280
|
-
const events = eventsBufferRef.current.splice(0, MAX_EVENTS_PER_CHUNK);
|
|
295
|
+
const events = isBeacon ? eventsBufferRef.current.splice(0) : eventsBufferRef.current.splice(0, MAX_EVENTS_PER_CHUNK);
|
|
281
296
|
const chunkIndex = chunkIndexRef.current++;
|
|
282
|
-
const chunkId = `${sessionId}_${chunkIndex}_${Date.now()}`;
|
|
283
|
-
if (eventStoreRef.current) {
|
|
284
|
-
try {
|
|
285
|
-
await eventStoreRef.current.saveChunk({
|
|
286
|
-
id: chunkId,
|
|
287
|
-
sessionId,
|
|
288
|
-
siteId,
|
|
289
|
-
apiKey,
|
|
290
|
-
events,
|
|
291
|
-
chunkIndex,
|
|
292
|
-
timestamp: Date.now()
|
|
293
|
-
});
|
|
294
|
-
} catch {
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
297
|
const payload = JSON.stringify({
|
|
298
298
|
sessionId,
|
|
299
299
|
siteId,
|
|
@@ -302,15 +302,26 @@ function NevisionRecorder({
|
|
|
302
302
|
chunkIndex
|
|
303
303
|
});
|
|
304
304
|
if (isBeacon && navigator.sendBeacon) {
|
|
305
|
-
|
|
305
|
+
navigator.sendBeacon(
|
|
306
306
|
`${url}/public/recordings/chunk`,
|
|
307
307
|
new Blob([payload], { type: "application/json" })
|
|
308
308
|
);
|
|
309
|
-
if (sent && eventStoreRef.current) {
|
|
310
|
-
eventStoreRef.current.deleteChunk(chunkId).catch(() => {
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
309
|
} else {
|
|
310
|
+
const chunkId = `${sessionId}_${chunkIndex}_${Date.now()}`;
|
|
311
|
+
if (eventStoreRef.current) {
|
|
312
|
+
try {
|
|
313
|
+
await eventStoreRef.current.saveChunk({
|
|
314
|
+
id: chunkId,
|
|
315
|
+
sessionId,
|
|
316
|
+
siteId,
|
|
317
|
+
apiKey,
|
|
318
|
+
events,
|
|
319
|
+
chunkIndex,
|
|
320
|
+
timestamp: Date.now()
|
|
321
|
+
});
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
314
325
|
try {
|
|
315
326
|
const response = await fetch(`${url}/public/recordings/chunk`, {
|
|
316
327
|
method: "POST",
|
|
@@ -339,10 +350,6 @@ function NevisionRecorder({
|
|
|
339
350
|
isActive = false;
|
|
340
351
|
clearInterval(intervalId);
|
|
341
352
|
stopFnRef.current?.();
|
|
342
|
-
if (sessionIdRef.current) {
|
|
343
|
-
sendChunk(apiUrl, sessionIdRef.current, true);
|
|
344
|
-
endSession(apiUrl, sessionIdRef.current);
|
|
345
|
-
}
|
|
346
353
|
sessionIdRef.current = null;
|
|
347
354
|
chunkIndexRef.current = 0;
|
|
348
355
|
eventsBufferRef.current = [];
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.tsx
|
|
4
4
|
import { useEffect, useRef } from "react";
|
|
5
5
|
var DEFAULT_API_URL = "https://api.ne-room.io";
|
|
6
|
-
var CHUNK_INTERVAL =
|
|
6
|
+
var CHUNK_INTERVAL = 2e3;
|
|
7
7
|
var MAX_EVENTS_PER_CHUNK = 100;
|
|
8
8
|
var DB_NAME = "nevision_recordings";
|
|
9
9
|
var STORE_NAME = "pending_events";
|
|
@@ -139,7 +139,6 @@ function NevisionRecorder({
|
|
|
139
139
|
const recordConfig = {
|
|
140
140
|
emit: (event) => {
|
|
141
141
|
eventsBufferRef.current.push(event);
|
|
142
|
-
persistEventsToStorage();
|
|
143
142
|
},
|
|
144
143
|
sampling: currentSampling ? {
|
|
145
144
|
mousemove: currentSampling.mousemove,
|
|
@@ -163,6 +162,36 @@ function NevisionRecorder({
|
|
|
163
162
|
if (stopFn) {
|
|
164
163
|
stopFnRef.current = stopFn;
|
|
165
164
|
}
|
|
165
|
+
const emitPageLifecycleEvent = (eventName) => {
|
|
166
|
+
const customEvent = {
|
|
167
|
+
type: 5,
|
|
168
|
+
// Custom event type in rrweb
|
|
169
|
+
data: {
|
|
170
|
+
tag: "page_lifecycle",
|
|
171
|
+
payload: {
|
|
172
|
+
event: eventName
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
timestamp: Date.now()
|
|
176
|
+
};
|
|
177
|
+
eventsBufferRef.current.push(customEvent);
|
|
178
|
+
};
|
|
179
|
+
const handleVisibilityChangeForTimeline = () => {
|
|
180
|
+
if (document.visibilityState === "hidden") {
|
|
181
|
+
emitPageLifecycleEvent("page_hidden");
|
|
182
|
+
} else {
|
|
183
|
+
emitPageLifecycleEvent("page_visible");
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const handleWindowFocusForTimeline = () => {
|
|
187
|
+
emitPageLifecycleEvent("page_focus");
|
|
188
|
+
};
|
|
189
|
+
const handleWindowBlurForTimeline = () => {
|
|
190
|
+
emitPageLifecycleEvent("page_blur");
|
|
191
|
+
};
|
|
192
|
+
document.addEventListener("visibilitychange", handleVisibilityChangeForTimeline);
|
|
193
|
+
window.addEventListener("focus", handleWindowFocusForTimeline);
|
|
194
|
+
window.addEventListener("blur", handleWindowBlurForTimeline);
|
|
166
195
|
intervalId = setInterval(() => {
|
|
167
196
|
sendChunk(apiUrl, sessionId);
|
|
168
197
|
}, CHUNK_INTERVAL);
|
|
@@ -177,44 +206,30 @@ function NevisionRecorder({
|
|
|
177
206
|
endSession(apiUrl, sessionIdRef.current);
|
|
178
207
|
}
|
|
179
208
|
};
|
|
180
|
-
|
|
209
|
+
const handleVisibilityChange = () => {
|
|
181
210
|
if (document.visibilityState === "hidden") {
|
|
182
211
|
handleFinalSync(false);
|
|
183
212
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
213
|
+
};
|
|
214
|
+
const handleBeforeUnload = () => {
|
|
186
215
|
handleFinalSync(true);
|
|
187
|
-
}
|
|
188
|
-
|
|
216
|
+
};
|
|
217
|
+
const handlePageHide = (e) => {
|
|
189
218
|
handleFinalSync(!e.persisted);
|
|
190
|
-
}
|
|
219
|
+
};
|
|
220
|
+
const handleWindowBlur = () => {
|
|
221
|
+
if (eventsBufferRef.current.length > 0 && sessionIdRef.current) {
|
|
222
|
+
sendChunk(apiUrl, sessionIdRef.current, true);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
226
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
227
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
228
|
+
window.addEventListener("blur", handleWindowBlur);
|
|
191
229
|
} catch (error) {
|
|
192
230
|
onErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
193
231
|
}
|
|
194
232
|
};
|
|
195
|
-
let persistTimeout = null;
|
|
196
|
-
const persistEventsToStorage = () => {
|
|
197
|
-
if (persistTimeout) return;
|
|
198
|
-
persistTimeout = setTimeout(async () => {
|
|
199
|
-
persistTimeout = null;
|
|
200
|
-
if (!sessionIdRef.current || !eventStoreRef.current) return;
|
|
201
|
-
if (eventsBufferRef.current.length === 0) return;
|
|
202
|
-
const events = [...eventsBufferRef.current];
|
|
203
|
-
const chunkId = `${sessionIdRef.current}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
204
|
-
try {
|
|
205
|
-
await eventStoreRef.current.saveChunk({
|
|
206
|
-
id: chunkId,
|
|
207
|
-
sessionId: sessionIdRef.current,
|
|
208
|
-
siteId,
|
|
209
|
-
apiKey,
|
|
210
|
-
events,
|
|
211
|
-
chunkIndex: chunkIndexRef.current,
|
|
212
|
-
timestamp: Date.now()
|
|
213
|
-
});
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
216
|
-
}, 1e3);
|
|
217
|
-
};
|
|
218
233
|
const retrySendPendingChunks = async (url) => {
|
|
219
234
|
if (!eventStoreRef.current) return;
|
|
220
235
|
try {
|
|
@@ -243,23 +258,8 @@ function NevisionRecorder({
|
|
|
243
258
|
};
|
|
244
259
|
const sendChunk = async (url, sessionId, isBeacon = false) => {
|
|
245
260
|
if (eventsBufferRef.current.length === 0) return;
|
|
246
|
-
const events = eventsBufferRef.current.splice(0, MAX_EVENTS_PER_CHUNK);
|
|
261
|
+
const events = isBeacon ? eventsBufferRef.current.splice(0) : eventsBufferRef.current.splice(0, MAX_EVENTS_PER_CHUNK);
|
|
247
262
|
const chunkIndex = chunkIndexRef.current++;
|
|
248
|
-
const chunkId = `${sessionId}_${chunkIndex}_${Date.now()}`;
|
|
249
|
-
if (eventStoreRef.current) {
|
|
250
|
-
try {
|
|
251
|
-
await eventStoreRef.current.saveChunk({
|
|
252
|
-
id: chunkId,
|
|
253
|
-
sessionId,
|
|
254
|
-
siteId,
|
|
255
|
-
apiKey,
|
|
256
|
-
events,
|
|
257
|
-
chunkIndex,
|
|
258
|
-
timestamp: Date.now()
|
|
259
|
-
});
|
|
260
|
-
} catch {
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
263
|
const payload = JSON.stringify({
|
|
264
264
|
sessionId,
|
|
265
265
|
siteId,
|
|
@@ -268,15 +268,26 @@ function NevisionRecorder({
|
|
|
268
268
|
chunkIndex
|
|
269
269
|
});
|
|
270
270
|
if (isBeacon && navigator.sendBeacon) {
|
|
271
|
-
|
|
271
|
+
navigator.sendBeacon(
|
|
272
272
|
`${url}/public/recordings/chunk`,
|
|
273
273
|
new Blob([payload], { type: "application/json" })
|
|
274
274
|
);
|
|
275
|
-
if (sent && eventStoreRef.current) {
|
|
276
|
-
eventStoreRef.current.deleteChunk(chunkId).catch(() => {
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
275
|
} else {
|
|
276
|
+
const chunkId = `${sessionId}_${chunkIndex}_${Date.now()}`;
|
|
277
|
+
if (eventStoreRef.current) {
|
|
278
|
+
try {
|
|
279
|
+
await eventStoreRef.current.saveChunk({
|
|
280
|
+
id: chunkId,
|
|
281
|
+
sessionId,
|
|
282
|
+
siteId,
|
|
283
|
+
apiKey,
|
|
284
|
+
events,
|
|
285
|
+
chunkIndex,
|
|
286
|
+
timestamp: Date.now()
|
|
287
|
+
});
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
280
291
|
try {
|
|
281
292
|
const response = await fetch(`${url}/public/recordings/chunk`, {
|
|
282
293
|
method: "POST",
|
|
@@ -305,10 +316,6 @@ function NevisionRecorder({
|
|
|
305
316
|
isActive = false;
|
|
306
317
|
clearInterval(intervalId);
|
|
307
318
|
stopFnRef.current?.();
|
|
308
|
-
if (sessionIdRef.current) {
|
|
309
|
-
sendChunk(apiUrl, sessionIdRef.current, true);
|
|
310
|
-
endSession(apiUrl, sessionIdRef.current);
|
|
311
|
-
}
|
|
312
319
|
sessionIdRef.current = null;
|
|
313
320
|
chunkIndexRef.current = 0;
|
|
314
321
|
eventsBufferRef.current = [];
|