@carpetai/rrweb-recorder
Version:
A React component for recording user sessions using rrweb. Meant to be used with CarpetAI's Analytics Agent.
627 lines (610 loc) • 21.6 kB
JavaScript
;
var require$$0 = require('react');
var rrweb = require('rrweb');
var jsxRuntime = {exports: {}};
var reactJsxRuntime_production = {};
/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_production;
function requireReactJsxRuntime_production () {
if (hasRequiredReactJsxRuntime_production) return reactJsxRuntime_production;
hasRequiredReactJsxRuntime_production = 1;
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function jsxProd(type, config, maybeKey) {
var key = null;
void 0 !== maybeKey && (key = "" + maybeKey);
void 0 !== config.key && (key = "" + config.key);
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
config = maybeKey.ref;
return {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: void 0 !== config ? config : null,
props: maybeKey
};
}
reactJsxRuntime_production.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_production.jsx = jsxProd;
reactJsxRuntime_production.jsxs = jsxProd;
return reactJsxRuntime_production;
}
var reactJsxRuntime_development = {};
/**
* @license React
* react-jsx-runtime.development.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_development;
function requireReactJsxRuntime_development () {
if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
hasRequiredReactJsxRuntime_development = 1;
"production" !== process.env.NODE_ENV &&
(function () {
function getComponentNameFromType(type) {
if (null == type) return null;
if ("function" === typeof type)
return type.$$typeof === REACT_CLIENT_REFERENCE
? null
: type.displayName || type.name || null;
if ("string" === typeof type) return type;
switch (type) {
case REACT_FRAGMENT_TYPE:
return "Fragment";
case REACT_PROFILER_TYPE:
return "Profiler";
case REACT_STRICT_MODE_TYPE:
return "StrictMode";
case REACT_SUSPENSE_TYPE:
return "Suspense";
case REACT_SUSPENSE_LIST_TYPE:
return "SuspenseList";
case REACT_ACTIVITY_TYPE:
return "Activity";
}
if ("object" === typeof type)
switch (
("number" === typeof type.tag &&
console.error(
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
),
type.$$typeof)
) {
case REACT_PORTAL_TYPE:
return "Portal";
case REACT_CONTEXT_TYPE:
return (type.displayName || "Context") + ".Provider";
case REACT_CONSUMER_TYPE:
return (type._context.displayName || "Context") + ".Consumer";
case REACT_FORWARD_REF_TYPE:
var innerType = type.render;
type = type.displayName;
type ||
((type = innerType.displayName || innerType.name || ""),
(type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef"));
return type;
case REACT_MEMO_TYPE:
return (
(innerType = type.displayName || null),
null !== innerType
? innerType
: getComponentNameFromType(type.type) || "Memo"
);
case REACT_LAZY_TYPE:
innerType = type._payload;
type = type._init;
try {
return getComponentNameFromType(type(innerType));
} catch (x) {}
}
return null;
}
function testStringCoercion(value) {
return "" + value;
}
function checkKeyStringCoercion(value) {
try {
testStringCoercion(value);
var JSCompiler_inline_result = !1;
} catch (e) {
JSCompiler_inline_result = !0;
}
if (JSCompiler_inline_result) {
JSCompiler_inline_result = console;
var JSCompiler_temp_const = JSCompiler_inline_result.error;
var JSCompiler_inline_result$jscomp$0 =
("function" === typeof Symbol &&
Symbol.toStringTag &&
value[Symbol.toStringTag]) ||
value.constructor.name ||
"Object";
JSCompiler_temp_const.call(
JSCompiler_inline_result,
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
JSCompiler_inline_result$jscomp$0
);
return testStringCoercion(value);
}
}
function getTaskName(type) {
if (type === REACT_FRAGMENT_TYPE) return "<>";
if (
"object" === typeof type &&
null !== type &&
type.$$typeof === REACT_LAZY_TYPE
)
return "<...>";
try {
var name = getComponentNameFromType(type);
return name ? "<" + name + ">" : "<...>";
} catch (x) {
return "<...>";
}
}
function getOwner() {
var dispatcher = ReactSharedInternals.A;
return null === dispatcher ? null : dispatcher.getOwner();
}
function UnknownOwner() {
return Error("react-stack-top-frame");
}
function hasValidKey(config) {
if (hasOwnProperty.call(config, "key")) {
var getter = Object.getOwnPropertyDescriptor(config, "key").get;
if (getter && getter.isReactWarning) return !1;
}
return void 0 !== config.key;
}
function defineKeyPropWarningGetter(props, displayName) {
function warnAboutAccessingKey() {
specialPropKeyWarningShown ||
((specialPropKeyWarningShown = !0),
console.error(
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
displayName
));
}
warnAboutAccessingKey.isReactWarning = !0;
Object.defineProperty(props, "key", {
get: warnAboutAccessingKey,
configurable: !0
});
}
function elementRefGetterWithDeprecationWarning() {
var componentName = getComponentNameFromType(this.type);
didWarnAboutElementRef[componentName] ||
((didWarnAboutElementRef[componentName] = !0),
console.error(
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
));
componentName = this.props.ref;
return void 0 !== componentName ? componentName : null;
}
function ReactElement(
type,
key,
self,
source,
owner,
props,
debugStack,
debugTask
) {
self = props.ref;
type = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
props: props,
_owner: owner
};
null !== (void 0 !== self ? self : null)
? Object.defineProperty(type, "ref", {
enumerable: !1,
get: elementRefGetterWithDeprecationWarning
})
: Object.defineProperty(type, "ref", { enumerable: !1, value: null });
type._store = {};
Object.defineProperty(type._store, "validated", {
configurable: !1,
enumerable: !1,
writable: !0,
value: 0
});
Object.defineProperty(type, "_debugInfo", {
configurable: !1,
enumerable: !1,
writable: !0,
value: null
});
Object.defineProperty(type, "_debugStack", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugStack
});
Object.defineProperty(type, "_debugTask", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugTask
});
Object.freeze && (Object.freeze(type.props), Object.freeze(type));
return type;
}
function jsxDEVImpl(
type,
config,
maybeKey,
isStaticChildren,
source,
self,
debugStack,
debugTask
) {
var children = config.children;
if (void 0 !== children)
if (isStaticChildren)
if (isArrayImpl(children)) {
for (
isStaticChildren = 0;
isStaticChildren < children.length;
isStaticChildren++
)
validateChildKeys(children[isStaticChildren]);
Object.freeze && Object.freeze(children);
} else
console.error(
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
);
else validateChildKeys(children);
if (hasOwnProperty.call(config, "key")) {
children = getComponentNameFromType(type);
var keys = Object.keys(config).filter(function (k) {
return "key" !== k;
});
isStaticChildren =
0 < keys.length
? "{key: someKey, " + keys.join(": ..., ") + ": ...}"
: "{key: someKey}";
didWarnAboutKeySpread[children + isStaticChildren] ||
((keys =
0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}"),
console.error(
'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />',
isStaticChildren,
children,
keys,
children
),
(didWarnAboutKeySpread[children + isStaticChildren] = !0));
}
children = null;
void 0 !== maybeKey &&
(checkKeyStringCoercion(maybeKey), (children = "" + maybeKey));
hasValidKey(config) &&
(checkKeyStringCoercion(config.key), (children = "" + config.key));
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
children &&
defineKeyPropWarningGetter(
maybeKey,
"function" === typeof type
? type.displayName || type.name || "Unknown"
: type
);
return ReactElement(
type,
children,
self,
source,
getOwner(),
maybeKey,
debugStack,
debugTask
);
}
function validateChildKeys(node) {
"object" === typeof node &&
null !== node &&
node.$$typeof === REACT_ELEMENT_TYPE &&
node._store &&
(node._store.validated = 1);
}
var React = require$$0,
REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_PORTAL_TYPE = Symbol.for("react.portal"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"),
REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"),
REACT_PROFILER_TYPE = Symbol.for("react.profiler");
var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"),
REACT_CONTEXT_TYPE = Symbol.for("react.context"),
REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"),
REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"),
REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"),
REACT_MEMO_TYPE = Symbol.for("react.memo"),
REACT_LAZY_TYPE = Symbol.for("react.lazy"),
REACT_ACTIVITY_TYPE = Symbol.for("react.activity"),
REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"),
ReactSharedInternals =
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
hasOwnProperty = Object.prototype.hasOwnProperty,
isArrayImpl = Array.isArray,
createTask = console.createTask
? console.createTask
: function () {
return null;
};
React = {
"react-stack-bottom-frame": function (callStackForError) {
return callStackForError();
}
};
var specialPropKeyWarningShown;
var didWarnAboutElementRef = {};
var unknownOwnerDebugStack = React["react-stack-bottom-frame"].bind(
React,
UnknownOwner
)();
var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
var didWarnAboutKeySpread = {};
reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_development.jsx = function (type, config, maybeKey, source, self) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!1,
source,
self,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
reactJsxRuntime_development.jsxs = function (type, config, maybeKey, source, self) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!0,
source,
self,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
})();
return reactJsxRuntime_development;
}
if (process.env.NODE_ENV === 'production') {
jsxRuntime.exports = requireReactJsxRuntime_production();
} else {
jsxRuntime.exports = requireReactJsxRuntime_development();
}
var jsxRuntimeExports = jsxRuntime.exports;
const defaultSaveSessionData = async (sessionData, apiKey, apiUrl) => {
const url = apiUrl || '/api/session-replay';
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify(sessionData)
});
if (!response.ok) {
throw new Error(`Failed to save session data: ${response.statusText}`);
}
}
catch (error) {
throw error;
}
};
const getOrCreateSessionId = (providedSessionId) => {
if (typeof window === 'undefined')
return '';
if (providedSessionId) {
return providedSessionId;
}
let sessionId = sessionStorage.getItem('rrweb_session_id');
if (!sessionId) {
sessionId = crypto.randomUUID();
sessionStorage.setItem('rrweb_session_id', sessionId);
}
return sessionId;
};
const shouldExcludePath = (excludePaths) => {
if (!excludePaths || typeof window === 'undefined')
return false;
const currentUrl = window.location.href;
return excludePaths.some(path => currentUrl.startsWith(path));
};
const createMetaEvent = () => {
if (typeof window === 'undefined')
return null;
return {
type: 4,
timestamp: Date.now(),
data: {
href: window.location.href,
url: window.location.href,
width: window.innerWidth,
height: window.innerHeight,
title: document.title,
userAgent: navigator.userAgent,
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
}
};
};
function useSessionRecorder({ autoStart = true, apiKey, apiUrl = "https://apt-agent-api-c5f32b392702.herokuapp.com/api/sessions", maxSessionDuration = 30 * 60 * 1000, saveInterval = 5000, excludePaths = ['http://localhost', 'https://localhost', 'http://127.0.0.1', 'https://127.0.0.1'], recordCanvas = false, recordCrossOriginIframes = false, sessionId: customSessionId, onSessionStart, onSessionStop, onError }) {
const [isRecording, setIsRecording] = require$$0.useState(false);
const sessionIdRef = require$$0.useRef(null);
const eventsRef = require$$0.useRef([]);
const recorderRef = require$$0.useRef(undefined);
const saveIntervalRef = require$$0.useRef(null);
const sessionStartTimeRef = require$$0.useRef(0);
const lastSavedIndexRef = require$$0.useRef(0);
const emit = (event) => {
eventsRef.current.push(event);
};
const saveEvents = async () => {
const currentSessionId = sessionIdRef.current;
if (eventsRef.current.length === 0 || !currentSessionId)
return;
try {
const newEvents = eventsRef.current.slice(lastSavedIndexRef.current);
if (newEvents.length === 0)
return;
const sessionData = {
sessionId: currentSessionId,
events: newEvents,
timestamp: Date.now(),
url: window.location.href
};
if (apiKey) {
await defaultSaveSessionData(sessionData, apiKey, apiUrl);
}
lastSavedIndexRef.current = eventsRef.current.length;
}
catch (error) {
onError === null || onError === void 0 ? void 0 : onError(error);
}
};
const addMetaEvent = () => {
if (!isRecording || typeof window === 'undefined')
return;
const metaEvent = createMetaEvent();
if (metaEvent) {
eventsRef.current.push(metaEvent);
}
};
const startRecording = () => {
if (isRecording || typeof window === 'undefined')
return;
const currentSessionId = customSessionId || getOrCreateSessionId();
sessionIdRef.current = currentSessionId;
sessionStartTimeRef.current = Date.now();
lastSavedIndexRef.current = 0;
eventsRef.current = [];
try {
recorderRef.current = rrweb.record({
emit,
recordCanvas,
recordCrossOriginIframes
});
setIsRecording(true);
onSessionStart === null || onSessionStart === void 0 ? void 0 : onSessionStart(currentSessionId);
addMetaEvent();
saveIntervalRef.current = setInterval(saveEvents, saveInterval);
setTimeout(() => {
if (isRecording) {
stopRecording();
}
}, maxSessionDuration);
}
catch (error) {
onError === null || onError === void 0 ? void 0 : onError(error);
}
};
const stopRecording = () => {
if (!isRecording)
return;
if (recorderRef.current) {
recorderRef.current();
recorderRef.current = undefined;
}
if (saveIntervalRef.current) {
clearInterval(saveIntervalRef.current);
saveIntervalRef.current = null;
}
setIsRecording(false);
onSessionStop === null || onSessionStop === void 0 ? void 0 : onSessionStop(sessionIdRef.current);
saveEvents();
};
require$$0.useEffect(() => {
if (autoStart && !shouldExcludePath(excludePaths)) {
const timer = setTimeout(() => {
startRecording();
}, 1000);
return () => clearTimeout(timer);
}
}, [autoStart, excludePaths]);
require$$0.useEffect(() => {
if (typeof window === 'undefined')
return;
const handlePopState = () => {
if (isRecording) {
setTimeout(() => {
addMetaEvent();
}, 100);
}
};
const handleHashChange = () => {
if (isRecording) {
setTimeout(() => {
addMetaEvent();
}, 100);
}
};
window.addEventListener('popstate', handlePopState);
window.addEventListener('hashchange', handleHashChange);
return () => {
window.removeEventListener('popstate', handlePopState);
window.removeEventListener('hashchange', handleHashChange);
};
}, [isRecording]);
require$$0.useEffect(() => {
return () => {
if (isRecording) {
stopRecording();
}
};
}, []);
return {
isRecording,
sessionId: sessionIdRef.current,
startRecording,
stopRecording
};
}
function SessionRecorderInner(props) {
useSessionRecorder(props);
return null;
}
function SessionRecorder(props) {
return (jsxRuntimeExports.jsx(SessionRecorderInner, { ...props }));
}
exports.SessionRecorder = SessionRecorder;
exports.useSessionRecorder = useSessionRecorder;
//# sourceMappingURL=index.js.map