Skip to content
Open
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
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
"@prisma/client": "^7.6.0",
"@prisma/extension-read-replicas": "^0.5.0",
"@react-spring/web": "^10.0.3",
"@rrweb/rrweb-plugin-console-record": "2.0.0-alpha.20",
"@rrweb/rrweb-plugin-console-replay": "2.0.0-alpha.20",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.96.0",
"@umami/react-zen": "^0.245.0",
Expand Down Expand Up @@ -106,8 +108,8 @@
"react-window": "^1.8.6",
"redis": "^4.5.1",
"request-ip": "^3.3.0",
"rrweb": "2.0.0-alpha.4",
"rrweb-player": "1.0.0-alpha.4",
"rrweb": "2.0.0-alpha.20",
Comment on lines 108 to +111
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Unrelated major rrweb version bump bundled with truncation fix

This PR upgrades rrweb from 2.0.0-alpha.4 to 2.0.0-alpha.20 and adds two new packages (@rrweb/rrweb-plugin-console-record, @rrweb/rrweb-plugin-console-replay), which is a substantial jump across 16 alpha releases. Because these changes are bundled with the unrelated Prisma truncation fix, a regression in the replay player or the recorder would be hard to bisect. The console recording feature also alters what data is captured and stored per session. This should be split into a separate PR so the rrweb upgrade can be reviewed and tested independently.

"rrweb-player": "2.0.0-alpha.20",
"semver": "^7.7.4",
"serialize-error": "^12.0.0",
"thenby": "^1.3.4",
Expand Down
94 changes: 71 additions & 23 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions public/intl/messages/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
"pixel": "Pixel",
"pixels": "Pixels",
"play": "Play",
"record-console": "Record console logs",
"poor": "Poor",
"powered-by": "Powered by {name}",
"preferences": "Preferences",
Expand Down
4 changes: 3 additions & 1 deletion public/intl/messages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@
"remove": "Remove",
"remove-member": "Remove member",
"play": "Play",
"record-console": "Record console logs",
"replay": "Replay",
"replay-id": "Replay ID",
"replay-code": "Session replay code",
Expand Down Expand Up @@ -429,6 +430,7 @@
"user-deleted": "User deleted.",
"viewed-page": "Viewed page",
"upgrade-required": "This feature requires a {plan} plan subscription.",
"visitor-log": "Visitor from <b>{country}</b> using <b>{browser}</b> on <b>{os}</b> <b>{device}</b>"
"visitor-log": "Visitor from <b>{country}</b> using <b>{browser}</b> on <b>{os}</b> <b>{device}</b>",
"record-console-warning": "This will record all console logs, which may include sensitive information."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,35 @@ export function ReplayPlayer({ events }: { events: any[] }) {
useEffect(() => {
if (!containerRef.current || !events?.length) return;

import('rrweb-player').then(mod => {
const RRWebPlayer = mod.default;
Promise.all([import('rrweb-player'), import('@rrweb/rrweb-plugin-console-replay')]).then(
([mod, consoleMod]) => {
const RRWebPlayer = mod.default;
const { getReplayConsolePlugin } = consoleMod;

if (containerRef.current) {
containerRef.current.innerHTML = '';
}
if (containerRef.current) {
containerRef.current.innerHTML = '';
}

playerRef.current = new RRWebPlayer({
target: containerRef.current,
props: {
events: events,
width: playerWidth,
height: playerHeight,
autoPlay: false,
showController: true,
speedOption: [1, 2, 4, 8],
useVirtualDom: false,
showWarning: false,
},
});
const hasConsole = events.some(event => event.data?.plugin === 'rrweb/console@1');

playerRef.current = new RRWebPlayer({
target: containerRef.current,
props: {
events: events,
width: playerWidth,
height: playerHeight,
autoPlay: false,
showController: true,
speedOption: [1, 2, 4, 8],
useVirtualDom: false,
showWarning: false,
plugins: hasConsole ? [getReplayConsolePlugin()] : [],
},
});

setLoaded(true);
});
setLoaded(true);
},
);

return () => {
if (playerRef.current) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface ReplayConfig {
maskLevel?: string;
maxDuration?: number;
blockSelector?: string;
recordConsole?: boolean;
}

export function WebsiteReplaySettings({ websiteId }: { websiteId: string }) {
Expand All @@ -37,13 +38,15 @@ export function WebsiteReplaySettings({ websiteId }: { websiteId: string }) {
const [maskLevel, setMaskLevel] = useState(config.maskLevel ?? 'moderate');
const [maxDuration, setMaxDuration] = useState(String(config.maxDuration ?? 300000));
const [blockSelector, setBlockSelector] = useState(config.blockSelector ?? '');
const [recordConsole, setRecordConsole] = useState(config.recordConsole ?? false);

const recorderUrl = cloudMode
? `${process.env.cloudUrl}/${RECORDER_NAME}`
: `${window?.location?.origin || ''}${process.env.basePath || ''}/${RECORDER_NAME}`;

let recorderAttrs = `data-website-id="${websiteId}" data-sample-rate="${sampleRate}" data-mask-level="${maskLevel}" data-max-duration="${parseInt(maxDuration, 10) || 300000}"`;
if (blockSelector) recorderAttrs += ` data-block-selector="${blockSelector}"`;
if (recordConsole) recorderAttrs += ` data-record-console="true"`;
const recorderCode = `<script defer src="${recorderUrl}" ${recorderAttrs}></script>`;

const handleToggle = async (value: boolean) => {
Expand Down Expand Up @@ -77,6 +80,7 @@ export function WebsiteReplaySettings({ websiteId }: { websiteId: string }) {
maskLevel,
maxDuration: parseInt(maxDuration, 10) || 300000,
...(blockSelector && { blockSelector }),
recordConsole,
},
},
{
Expand Down Expand Up @@ -151,6 +155,12 @@ export function WebsiteReplaySettings({ websiteId }: { websiteId: string }) {
<Label>{t(labels.blockSelector)}</Label>
<TextField value={blockSelector} onChange={setBlockSelector} />
</Column>
<Switch isSelected={recordConsole} onChange={setRecordConsole}>
{t(labels.recordConsole)}
</Switch>
<Text color="muted" size="sm">
{t(messages.recordConsoleWarning)}
</Text>
<Row>
<Button variant="primary" onPress={handleSave} isDisabled={isPending}>
{t(labels.save)}
Expand Down
1 change: 1 addition & 0 deletions src/components/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ export const labels: Record<string, string> = {
duration: 'label.duration',
recorded: 'label.recorded',
upgrade: 'label.upgrade',
recordConsole: 'label.record-console',
};

export const messages: Record<string, string> = {
Expand Down
12 changes: 12 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ export const DATETIME_REGEX =
export const URL_LENGTH = 500;
export const PAGE_TITLE_LENGTH = 500;
export const EVENT_NAME_LENGTH = 50;
export const UTM_LENGTH = 255;
export const CLICK_ID_LENGTH = 255;
export const TAG_LENGTH = 50;
export const HOSTNAME_LENGTH = 100;
export const DATA_LENGTH = 500;
export const CURRENCY_LENGTH = 10;
export const SESSION_FIELD_LENGTH = 20;
export const SCREEN_LENGTH = 11;
export const LANGUAGE_LENGTH = 35;
export const COUNTRY_LENGTH = 2;
export const CITY_LENGTH = 50;
export const DISTINCT_ID_LENGTH = 50;

export const UTM_PARAMS = ['utm_campaign', 'utm_content', 'utm_medium', 'utm_source', 'utm_term'];

Expand Down
Loading