@interfere/react 0.0.1
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/__tests__/client.test.d.ts +2 -0
- package/dist/__tests__/client.test.d.ts.map +1 -0
- package/dist/__tests__/client.test.js +447 -0
- package/dist/__tests__/client.test.js.map +1 -0
- package/dist/__tests__/lib/core/error-handlers.test.d.ts +2 -0
- package/dist/__tests__/lib/core/error-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/lib/core/error-handlers.test.js +596 -0
- package/dist/__tests__/lib/core/error-handlers.test.js.map +1 -0
- package/dist/__tests__/lib/core/event-queue.test.d.ts +2 -0
- package/dist/__tests__/lib/core/event-queue.test.d.ts.map +1 -0
- package/dist/__tests__/lib/core/event-queue.test.js +290 -0
- package/dist/__tests__/lib/core/event-queue.test.js.map +1 -0
- package/dist/__tests__/lib/core/runtime.test.d.ts +2 -0
- package/dist/__tests__/lib/core/runtime.test.d.ts.map +1 -0
- package/dist/__tests__/lib/core/runtime.test.js +133 -0
- package/dist/__tests__/lib/core/runtime.test.js.map +1 -0
- package/dist/__tests__/lib/core/session-manager.test.d.ts +2 -0
- package/dist/__tests__/lib/core/session-manager.test.d.ts.map +1 -0
- package/dist/__tests__/lib/core/session-manager.test.js +356 -0
- package/dist/__tests__/lib/core/session-manager.test.js.map +1 -0
- package/dist/__tests__/provider.test.d.ts +2 -0
- package/dist/__tests__/provider.test.d.ts.map +1 -0
- package/dist/__tests__/provider.test.js +143 -0
- package/dist/__tests__/provider.test.js.map +1 -0
- package/dist/client.d.ts +78 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +219 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/core/error-handlers.d.ts +14 -0
- package/dist/lib/core/error-handlers.d.ts.map +1 -0
- package/dist/lib/core/error-handlers.js +191 -0
- package/dist/lib/core/error-handlers.js.map +1 -0
- package/dist/lib/core/event-queue.d.ts +90 -0
- package/dist/lib/core/event-queue.d.ts.map +1 -0
- package/dist/lib/core/event-queue.js +286 -0
- package/dist/lib/core/event-queue.js.map +1 -0
- package/dist/lib/core/runtime.d.ts +7 -0
- package/dist/lib/core/runtime.d.ts.map +1 -0
- package/dist/lib/core/runtime.js +16 -0
- package/dist/lib/core/runtime.js.map +1 -0
- package/dist/lib/core/session-manager.d.ts +96 -0
- package/dist/lib/core/session-manager.d.ts.map +1 -0
- package/dist/lib/core/session-manager.js +431 -0
- package/dist/lib/core/session-manager.js.map +1 -0
- package/dist/lib/persistence/storage.d.ts +5 -0
- package/dist/lib/persistence/storage.d.ts.map +1 -0
- package/dist/lib/persistence/storage.js +67 -0
- package/dist/lib/persistence/storage.js.map +1 -0
- package/dist/lib/session/rage-click.d.ts +2 -0
- package/dist/lib/session/rage-click.d.ts.map +1 -0
- package/dist/lib/session/rage-click.js +51 -0
- package/dist/lib/session/rage-click.js.map +1 -0
- package/dist/lib/session/replay.d.ts +3 -0
- package/dist/lib/session/replay.d.ts.map +1 -0
- package/dist/lib/session/replay.js +106 -0
- package/dist/lib/session/replay.js.map +1 -0
- package/dist/lib/session/session-summary.d.ts +3 -0
- package/dist/lib/session/session-summary.d.ts.map +1 -0
- package/dist/lib/session/session-summary.js +79 -0
- package/dist/lib/session/session-summary.js.map +1 -0
- package/dist/provider.d.ts +76 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +138 -0
- package/dist/provider.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* Session replay capture using rrweb, chunked streaming to ingest */
|
|
2
|
+
"use client";
|
|
3
|
+
import { REPLAY_CHUNK_MS } from "@interfere/constants";
|
|
4
|
+
import { capture, flush } from "../../client.js";
|
|
5
|
+
let started = false;
|
|
6
|
+
let stopFn = null;
|
|
7
|
+
export function setupReplay() {
|
|
8
|
+
if (started) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
started = true;
|
|
15
|
+
Promise.all([import("rrweb"), import("@rrweb/packer")])
|
|
16
|
+
.then(([rrwebModule, packerModule]) => {
|
|
17
|
+
// Handle different module export patterns
|
|
18
|
+
const rrweb = rrwebModule;
|
|
19
|
+
const record = rrweb.record || rrweb;
|
|
20
|
+
const { pack } = packerModule;
|
|
21
|
+
const events = [];
|
|
22
|
+
// Check if record is a function
|
|
23
|
+
if (typeof record !== "function") {
|
|
24
|
+
// biome-ignore lint/suspicious/noConsole: error logging needed
|
|
25
|
+
console.error("rrweb.record is not available");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const stopRecord = record({
|
|
29
|
+
emit(event) {
|
|
30
|
+
// Compress the event before storing it
|
|
31
|
+
const packedEvent = pack(event);
|
|
32
|
+
events.push(packedEvent);
|
|
33
|
+
},
|
|
34
|
+
maskAllInputs: true, // Privacy: mask all text inputs by default
|
|
35
|
+
});
|
|
36
|
+
const sendChunk = () => {
|
|
37
|
+
if (events.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const chunk = events.splice(0, events.length);
|
|
41
|
+
try {
|
|
42
|
+
capture("replay_chunk", {
|
|
43
|
+
version: 1,
|
|
44
|
+
chunkTs: Date.now(),
|
|
45
|
+
count: chunk.length,
|
|
46
|
+
// rrweb events are pre-compressed with @rrweb/packer
|
|
47
|
+
events: chunk,
|
|
48
|
+
});
|
|
49
|
+
flush();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// swallow
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const intervalId = window.setInterval(sendChunk, REPLAY_CHUNK_MS);
|
|
56
|
+
const flushNow = () => {
|
|
57
|
+
try {
|
|
58
|
+
sendChunk();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// swallow
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const beforeUnload = () => flushNow();
|
|
65
|
+
const visibility = () => {
|
|
66
|
+
if (document.visibilityState === "hidden") {
|
|
67
|
+
flushNow();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
window.addEventListener("beforeunload", beforeUnload);
|
|
71
|
+
document.addEventListener("visibilitychange", visibility);
|
|
72
|
+
stopFn = () => {
|
|
73
|
+
try {
|
|
74
|
+
if (typeof stopRecord === "function") {
|
|
75
|
+
stopRecord();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
/* no-op */
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
window.clearInterval(intervalId);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* no-op */
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
window.removeEventListener("beforeunload", beforeUnload);
|
|
89
|
+
document.removeEventListener("visibilitychange", visibility);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* no-op */
|
|
93
|
+
}
|
|
94
|
+
started = false;
|
|
95
|
+
stopFn = null;
|
|
96
|
+
};
|
|
97
|
+
})
|
|
98
|
+
.catch((error) => {
|
|
99
|
+
// biome-ignore lint/suspicious/noConsole: Just for testing
|
|
100
|
+
console.error("Failed to load rrweb:", error);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export function stopReplay() {
|
|
104
|
+
stopFn?.();
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../../src/lib/session/replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-summary.d.ts","sourceRoot":"","sources":["../../../src/lib/session/session-summary.ts"],"names":[],"mappings":"AAsFA,wBAAgB,mBAAmB,6BA4BlC;AAED,wBAAgB,kBAAkB,SAKjC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Session summary using AI */
|
|
2
|
+
"use client";
|
|
3
|
+
import { SESSION_SUMMARY_INTERVAL } from "@interfere/constants";
|
|
4
|
+
import { capture, flush } from "../../client.js";
|
|
5
|
+
let intervalId = null;
|
|
6
|
+
let isCapturingSummary = false;
|
|
7
|
+
async function captureSessionSummary() {
|
|
8
|
+
if (isCapturingSummary) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
isCapturingSummary = true;
|
|
12
|
+
const w = window;
|
|
13
|
+
try {
|
|
14
|
+
if (!w.ai?.summarizer) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Get page content for summarization
|
|
18
|
+
const textContent = document.body.innerText || "";
|
|
19
|
+
const MIN_CONTENT_LENGTH = 100;
|
|
20
|
+
const MAX_CONTENT_LENGTH = 5000;
|
|
21
|
+
if (textContent.length < MIN_CONTENT_LENGTH) {
|
|
22
|
+
// Not enough content to summarize
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// @ts-expect-error - experimental API
|
|
26
|
+
const canSummarize = await window.ai.summarizer.capabilities();
|
|
27
|
+
if (canSummarize.available !== "readily") {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// @ts-expect-error - experimental API
|
|
31
|
+
const session = await window.ai.summarizer.create();
|
|
32
|
+
const summary = await session.summarize(textContent.substring(0, MAX_CONTENT_LENGTH));
|
|
33
|
+
capture("session_summary", {
|
|
34
|
+
summary,
|
|
35
|
+
pageTitle: document.title,
|
|
36
|
+
pageUrl: window.location.href,
|
|
37
|
+
contentLength: textContent.length,
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
});
|
|
40
|
+
flush();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// biome-ignore lint/suspicious/noConsole: Just for testing
|
|
44
|
+
console.warn("Failed to capture session summary - this is expected if Chrome AI is not available.", error);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
isCapturingSummary = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function setupSessionSummary() {
|
|
51
|
+
if (typeof window === "undefined") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const INITIAL_DELAY = 5000;
|
|
55
|
+
// Initial capture after a delay
|
|
56
|
+
setTimeout(captureSessionSummary, INITIAL_DELAY);
|
|
57
|
+
// Regular interval captures
|
|
58
|
+
intervalId = setInterval(captureSessionSummary, SESSION_SUMMARY_INTERVAL);
|
|
59
|
+
// Capture on visibility change
|
|
60
|
+
const handleVisibilityChange = () => {
|
|
61
|
+
if (document.visibilityState === "hidden") {
|
|
62
|
+
captureSessionSummary();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
66
|
+
return () => {
|
|
67
|
+
if (intervalId) {
|
|
68
|
+
clearInterval(intervalId);
|
|
69
|
+
}
|
|
70
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function stopSessionSummary() {
|
|
74
|
+
if (intervalId) {
|
|
75
|
+
clearInterval(intervalId);
|
|
76
|
+
intervalId = null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=session-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-summary.js","sourceRoot":"","sources":["../../../src/lib/session/session-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interfere Provider
|
|
3
|
+
*
|
|
4
|
+
* Sets up error boundaries, session monitoring features,
|
|
5
|
+
* and provides context for the SDK.
|
|
6
|
+
*/
|
|
7
|
+
import { type PropsWithChildren, type ReactNode } from "react";
|
|
8
|
+
import { capture, captureCustom, captureError, captureUIEvent, flush, getDebugInfo, getSessionId, getWindowId, resetSession } from "./client.js";
|
|
9
|
+
type InterfereContextValue = {
|
|
10
|
+
capture: typeof capture;
|
|
11
|
+
captureError: typeof captureError;
|
|
12
|
+
captureCustom: typeof captureCustom;
|
|
13
|
+
captureUIEvent: typeof captureUIEvent;
|
|
14
|
+
flush: typeof flush;
|
|
15
|
+
getSessionId: typeof getSessionId;
|
|
16
|
+
getWindowId: typeof getWindowId;
|
|
17
|
+
resetSession: typeof resetSession;
|
|
18
|
+
getDebugInfo: typeof getDebugInfo;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Provider props
|
|
22
|
+
*/
|
|
23
|
+
export interface InterfereProviderProps extends PropsWithChildren {
|
|
24
|
+
/**
|
|
25
|
+
* Whether to capture unhandled errors
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
captureErrors?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether to capture unhandled promise rejections
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
captureRejections?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether to enable session replay recording
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
enableReplay?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Whether to enable rage click detection
|
|
41
|
+
* @default true
|
|
42
|
+
*/
|
|
43
|
+
enableRageClick?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to enable session summary (requires Chrome with AI features)
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
enableSessionSummary?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Custom debug logger
|
|
51
|
+
*/
|
|
52
|
+
debugLogger?: (message: string, error?: Error) => void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Interfere Provider Component
|
|
56
|
+
*
|
|
57
|
+
* Usage:
|
|
58
|
+
* ```tsx
|
|
59
|
+
* export default function RootLayout({ children }: { children: ReactNode }) {
|
|
60
|
+
* return (
|
|
61
|
+
* <html>
|
|
62
|
+
* <body>
|
|
63
|
+
* <InterfereProvider>{children}</InterfereProvider>
|
|
64
|
+
* </body>
|
|
65
|
+
* </html>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function InterfereProvider({ children, captureErrors, captureRejections, enableReplay, enableRageClick, enableSessionSummary, debugLogger, }: InterfereProviderProps): ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Hook to access Interfere SDK functions
|
|
73
|
+
*/
|
|
74
|
+
export declare function useInterfere(): InterfereContextValue;
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAEA;;;;;GAKG;AAGH,OAAc,EAEZ,KAAK,iBAAiB,EACtB,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EAEb,MAAM,aAAa,CAAC;AAOrB,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,YAAY,EAAE,OAAO,YAAY,CAAC;CACnC,CAAC;AAcF;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CACxD;AAyDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,aAAoB,EACpB,iBAAwB,EACxB,YAAmB,EACnB,eAAsB,EACtB,oBAA2B,EAC3B,WAAW,GACZ,EAAE,sBAAsB,GAAG,SAAS,CAgEpC;AAED;;GAEG;AACH,wBAAgB,YAAY,0BAE3B"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Interfere Provider
|
|
5
|
+
*
|
|
6
|
+
* Sets up error boundaries, session monitoring features,
|
|
7
|
+
* and provides context for the SDK.
|
|
8
|
+
*/
|
|
9
|
+
// biome-ignore lint/correctness/noUnusedImports: React is required for JSX runtime
|
|
10
|
+
import React, { createContext, useContext, useEffect, } from "react";
|
|
11
|
+
import { capture, captureCustom, captureError, captureUIEvent, flush, getDebugInfo, getSessionId, getWindowId, resetSession, setDebugLogger, } from "./client.js";
|
|
12
|
+
import { setupRageClick } from "./lib/session/rage-click.js";
|
|
13
|
+
import { setupReplay, stopReplay } from "./lib/session/replay.js";
|
|
14
|
+
import { setupSessionSummary } from "./lib/session/session-summary.js";
|
|
15
|
+
const InterfereContext = createContext({
|
|
16
|
+
capture,
|
|
17
|
+
captureError,
|
|
18
|
+
captureCustom,
|
|
19
|
+
captureUIEvent,
|
|
20
|
+
flush,
|
|
21
|
+
getSessionId,
|
|
22
|
+
getWindowId,
|
|
23
|
+
resetSession,
|
|
24
|
+
getDebugInfo,
|
|
25
|
+
});
|
|
26
|
+
// Setup helper functions to reduce complexity
|
|
27
|
+
function setupErrorHandlers(handlers) {
|
|
28
|
+
const errorHandler = (event) => {
|
|
29
|
+
captureError({
|
|
30
|
+
message: event.message,
|
|
31
|
+
stack: event.error?.stack,
|
|
32
|
+
filename: event.filename,
|
|
33
|
+
lineno: event.lineno,
|
|
34
|
+
colno: event.colno,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
window.addEventListener("error", errorHandler);
|
|
38
|
+
handlers.push(() => window.removeEventListener("error", errorHandler));
|
|
39
|
+
}
|
|
40
|
+
function setupRejectionHandlers(handlers) {
|
|
41
|
+
const rejectionHandler = (event) => {
|
|
42
|
+
captureError({
|
|
43
|
+
message: `Unhandled Promise Rejection: ${event.reason}`,
|
|
44
|
+
stack: event.reason?.stack,
|
|
45
|
+
type: "unhandledrejection",
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
window.addEventListener("unhandledrejection", rejectionHandler);
|
|
49
|
+
handlers.push(() => window.removeEventListener("unhandledrejection", rejectionHandler));
|
|
50
|
+
}
|
|
51
|
+
function setupSessionFeatures(handlers, enableReplay, enableRageClick, enableSessionSummary) {
|
|
52
|
+
if (enableReplay) {
|
|
53
|
+
setupReplay();
|
|
54
|
+
handlers.push(() => stopReplay());
|
|
55
|
+
}
|
|
56
|
+
if (enableRageClick) {
|
|
57
|
+
const cleanupRageClick = setupRageClick();
|
|
58
|
+
if (cleanupRageClick) {
|
|
59
|
+
handlers.push(cleanupRageClick);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (enableSessionSummary) {
|
|
63
|
+
const cleanupSummary = setupSessionSummary();
|
|
64
|
+
if (cleanupSummary) {
|
|
65
|
+
handlers.push(cleanupSummary);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Interfere Provider Component
|
|
71
|
+
*
|
|
72
|
+
* Usage:
|
|
73
|
+
* ```tsx
|
|
74
|
+
* export default function RootLayout({ children }: { children: ReactNode }) {
|
|
75
|
+
* return (
|
|
76
|
+
* <html>
|
|
77
|
+
* <body>
|
|
78
|
+
* <InterfereProvider>{children}</InterfereProvider>
|
|
79
|
+
* </body>
|
|
80
|
+
* </html>
|
|
81
|
+
* );
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function InterfereProvider({ children, captureErrors = true, captureRejections = true, enableReplay = true, enableRageClick = true, enableSessionSummary = true, debugLogger, }) {
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (typeof window === "undefined") {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const handlers = [];
|
|
91
|
+
// Set debug logger if provided
|
|
92
|
+
if (debugLogger) {
|
|
93
|
+
setDebugLogger(debugLogger);
|
|
94
|
+
}
|
|
95
|
+
// Set up error handlers
|
|
96
|
+
if (captureErrors) {
|
|
97
|
+
setupErrorHandlers(handlers);
|
|
98
|
+
}
|
|
99
|
+
// Set up rejection handlers
|
|
100
|
+
if (captureRejections) {
|
|
101
|
+
setupRejectionHandlers(handlers);
|
|
102
|
+
}
|
|
103
|
+
// Set up session features
|
|
104
|
+
setupSessionFeatures(handlers, enableReplay, enableRageClick, enableSessionSummary);
|
|
105
|
+
// Cleanup
|
|
106
|
+
return () => {
|
|
107
|
+
for (const cleanup of handlers) {
|
|
108
|
+
cleanup();
|
|
109
|
+
}
|
|
110
|
+
flush(); // Flush any pending events on unmount
|
|
111
|
+
};
|
|
112
|
+
}, [
|
|
113
|
+
captureErrors,
|
|
114
|
+
captureRejections,
|
|
115
|
+
enableReplay,
|
|
116
|
+
enableRageClick,
|
|
117
|
+
enableSessionSummary,
|
|
118
|
+
debugLogger,
|
|
119
|
+
]);
|
|
120
|
+
return (_jsx(InterfereContext.Provider, { value: {
|
|
121
|
+
capture,
|
|
122
|
+
captureError,
|
|
123
|
+
captureCustom,
|
|
124
|
+
captureUIEvent,
|
|
125
|
+
flush,
|
|
126
|
+
getSessionId,
|
|
127
|
+
getWindowId,
|
|
128
|
+
resetSession,
|
|
129
|
+
getDebugInfo,
|
|
130
|
+
}, children: children }));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Hook to access Interfere SDK functions
|
|
134
|
+
*/
|
|
135
|
+
export function useInterfere() {
|
|
136
|
+
return useContext(InterfereContext);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;GAKG;AAEH,mFAAmF;AACnF,OAAO,KAAK,EAAE,EACZ,aAAa,EAGb,UAAU,EACV,SAAS,GACV,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAevE,MAAM,gBAAgB,GAAG,aAAa,CAAwB;IAC5D,OAAO;IACP,YAAY;IACZ,aAAa;IACb,cAAc;IACd,KAAK;IACL,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,YAAY;CACb,CAAC,CAAC;AA0CH,8CAA8C;AAC9C,SAAS,kBAAkB,CAAC,QAA2B;IACrD,MAAM,YAAY,GAAG,CAAC,KAAiB,EAAE,EAAE;QACzC,YAAY,CAAC;YACX,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK;YACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,sBAAsB,CAAC,QAA2B;IACzD,MAAM,gBAAgB,GAAG,CAAC,KAA4B,EAAE,EAAE;QACxD,YAAY,CAAC;YACX,OAAO,EAAE,gCAAgC,KAAK,CAAC,MAAM,EAAE;YACvD,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK;YAC1B,IAAI,EAAE,oBAAoB;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;IAChE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CACjB,MAAM,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CACnE,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAA2B,EAC3B,YAAqB,EACrB,eAAwB,EACxB,oBAA6B;IAE7B,IAAI,YAAY,EAAE,CAAC;QACjB,WAAW,EAAE,CAAC;QACd,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,gBAAgB,GAAG,cAAc,EAAE,CAAC;QAC1C,IAAI,gBAAgB,EAAE,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,IAAI,oBAAoB,EAAE,CAAC;QACzB,MAAM,cAAc,GAAG,mBAAmB,EAAE,CAAC;QAC7C,IAAI,cAAc,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,QAAQ,EACR,aAAa,GAAG,IAAI,EACpB,iBAAiB,GAAG,IAAI,EACxB,YAAY,GAAG,IAAI,EACnB,eAAe,GAAG,IAAI,EACtB,oBAAoB,GAAG,IAAI,EAC3B,WAAW,GACY;IACvB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAsB,EAAE,CAAC;QAEvC,+BAA+B;QAC/B,IAAI,WAAW,EAAE,CAAC;YAChB,cAAc,CAAC,WAAW,CAAC,CAAC;QAC9B,CAAC;QAED,wBAAwB;QACxB,IAAI,aAAa,EAAE,CAAC;YAClB,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAED,4BAA4B;QAC5B,IAAI,iBAAiB,EAAE,CAAC;YACtB,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,0BAA0B;QAC1B,oBAAoB,CAClB,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,oBAAoB,CACrB,CAAC;QAEF,UAAU;QACV,OAAO,GAAG,EAAE;YACV,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,KAAK,EAAE,CAAC,CAAC,sCAAsC;QACjD,CAAC,CAAC;IACJ,CAAC,EAAE;QACD,aAAa;QACb,iBAAiB;QACjB,YAAY;QACZ,eAAe;QACf,oBAAoB;QACpB,WAAW;KACZ,CAAC,CAAC;IAEH,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IACxB,KAAK,EAAE;YACL,OAAO;YACP,YAAY;YACZ,aAAa;YACb,cAAc;YACd,KAAK;YACL,YAAY;YACZ,WAAW;YACX,YAAY;YACZ,YAAY;SACb,YAEA,QAAQ,GACiB,CAC7B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@interfere/react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Build apps that never break.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"observability",
|
|
8
|
+
"typescript",
|
|
9
|
+
"react",
|
|
10
|
+
"logging",
|
|
11
|
+
"error-tracking",
|
|
12
|
+
"session-replay"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://interfere.com",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "mailto:support@interfere.com"
|
|
17
|
+
},
|
|
18
|
+
"author": "Interfere <support@interfere.com> (https://interfere.com)",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./client": {
|
|
31
|
+
"types": "./dist/client.d.ts",
|
|
32
|
+
"default": "./dist/client.js"
|
|
33
|
+
},
|
|
34
|
+
"./provider": {
|
|
35
|
+
"types": "./dist/provider.d.ts",
|
|
36
|
+
"default": "./dist/provider.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@rrweb/packer": "2.0.0-alpha.18",
|
|
44
|
+
"@rrweb/types": "2.0.0-alpha.18",
|
|
45
|
+
"nanoid": "^5.1.6",
|
|
46
|
+
"rrweb": "2.0.0-alpha.18",
|
|
47
|
+
"uuid": "^13.0.0",
|
|
48
|
+
"@interfere/constants": "0.0.1",
|
|
49
|
+
"@interfere/schemas": "0.0.7"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react": ">=18",
|
|
53
|
+
"react-dom": ">=18"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "22.15.29",
|
|
57
|
+
"@types/react": "19.1.16",
|
|
58
|
+
"@types/react-dom": "19.1.9",
|
|
59
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
60
|
+
"jsdom": "^27.0.0",
|
|
61
|
+
"react": "^19.1.1",
|
|
62
|
+
"react-dom": "^19.1.1",
|
|
63
|
+
"typescript": "5.9.3",
|
|
64
|
+
"vitest": "^3.2.4",
|
|
65
|
+
"@interfere/typescript-config": "1.0.2"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsc",
|
|
69
|
+
"dev": "tsc --watch",
|
|
70
|
+
"check-types": "tsc --noEmit --incremental",
|
|
71
|
+
"test": "vitest run --coverage"
|
|
72
|
+
}
|
|
73
|
+
}
|