From c6ffe19c3b9a6bdc79e669fce8fad23c54545996 Mon Sep 17 00:00:00 2001 From: reemhagbi Date: Sun, 16 Jun 2024 13:27:27 +0300 Subject: [PATCH 01/34] rrweb + snapshot --- packages/all/package.json | 2 +- packages/all/src/index.ts | 2 +- packages/all/test/cross-origin-iframe-packer.test.ts | 2 +- packages/all/test/utils.ts | 2 +- .../plugins/rrweb-plugin-canvas-webrtc-record/package.json | 4 ++-- .../plugins/rrweb-plugin-canvas-webrtc-record/src/index.ts | 2 +- .../plugins/rrweb-plugin-canvas-webrtc-replay/package.json | 4 ++-- .../plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts | 4 ++-- packages/plugins/rrweb-plugin-console-record/package.json | 4 ++-- packages/plugins/rrweb-plugin-console-record/src/index.ts | 2 +- .../plugins/rrweb-plugin-console-record/test/html/index.ts | 2 +- packages/plugins/rrweb-plugin-console-replay/package.json | 4 ++-- packages/plugins/rrweb-plugin-console-replay/src/index.ts | 2 +- .../plugins/rrweb-plugin-sequential-id-record/package.json | 4 ++-- .../plugins/rrweb-plugin-sequential-id-replay/package.json | 4 ++-- .../plugins/rrweb-plugin-sequential-id-replay/src/index.ts | 2 +- packages/record/package.json | 2 +- packages/record/src/index.ts | 2 +- packages/replay/package.json | 2 +- packages/replay/src/index.ts | 4 ++-- packages/rrdom-nodejs/package.json | 2 +- packages/rrdom-nodejs/src/document-nodejs.ts | 2 +- packages/rrdom-nodejs/test/document-nodejs.test.ts | 2 +- packages/rrdom/package.json | 2 +- packages/rrdom/src/diff.ts | 2 +- packages/rrdom/src/document.ts | 2 +- packages/rrdom/src/index.ts | 4 ++-- packages/rrdom/test/diff.test.ts | 2 +- packages/rrdom/test/document.test.ts | 2 +- packages/rrdom/test/virtual-dom.test.ts | 2 +- packages/rrweb-player/src/types.ts | 2 +- packages/rrweb-snapshot/package.json | 2 +- packages/rrweb-snapshot/test/utils.test.ts | 2 +- packages/rrweb/package.json | 4 ++-- packages/rrweb/src/record/iframe-manager.ts | 4 ++-- packages/rrweb/src/record/index.ts | 2 +- packages/rrweb/src/record/mutation.ts | 2 +- packages/rrweb/src/record/observer.ts | 2 +- packages/rrweb/src/record/observers/canvas/canvas-manager.ts | 2 +- packages/rrweb/src/record/observers/canvas/canvas.ts | 2 +- packages/rrweb/src/record/observers/canvas/webgl.ts | 2 +- packages/rrweb/src/record/shadow-dom-manager.ts | 4 ++-- packages/rrweb/src/record/stylesheet-manager.ts | 4 ++-- .../rrweb/src/record/workers/image-bitmap-data-url-worker.ts | 2 +- packages/rrweb/src/replay/index.ts | 2 +- packages/rrweb/src/replay/media/index.ts | 2 +- packages/rrweb/src/types.ts | 2 +- packages/rrweb/src/utils.ts | 4 ++-- packages/rrweb/test/integration.test.ts | 2 +- packages/rrweb/test/record/webgl.test.ts | 2 +- packages/rrweb/test/rrdom.test.ts | 2 +- packages/rrweb/test/utils.ts | 2 +- packages/types/package.json | 2 +- packages/types/src/index.ts | 2 +- packages/web-extension/package.json | 2 +- packages/web-extension/src/content/inject.ts | 4 ++-- 56 files changed, 71 insertions(+), 71 deletions(-) diff --git a/packages/all/package.json b/packages/all/package.json index 92ec802a4a..880d972993 100644 --- a/packages/all/package.json +++ b/packages/all/package.json @@ -58,7 +58,7 @@ "dependencies": { "@rrweb/types": "^2.0.0-alpha.15", "@rrweb/packer": "^2.0.0-alpha.15", - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/all/src/index.ts b/packages/all/src/index.ts index aa01eeaba9..291c6caff3 100644 --- a/packages/all/src/index.ts +++ b/packages/all/src/index.ts @@ -1,4 +1,4 @@ -export * from 'rrweb'; +export * from '@saola.ai/rrweb'; export * from '@rrweb/packer'; // export * from '@rrweb/rrweb-plugin-console-record'; // export * from '@rrweb/rrweb-plugin-console-replay'; diff --git a/packages/all/test/cross-origin-iframe-packer.test.ts b/packages/all/test/cross-origin-iframe-packer.test.ts index 9ae56b31bb..2bcaf266f4 100644 --- a/packages/all/test/cross-origin-iframe-packer.test.ts +++ b/packages/all/test/cross-origin-iframe-packer.test.ts @@ -16,7 +16,7 @@ import { unpack } from '@rrweb/packer'; import * as fs from 'fs'; import * as path from 'path'; import type * as puppeteer from 'puppeteer'; -import type { recordOptions } from 'rrweb'; +import type { recordOptions } from '@saola.ai/rrweb'; import type {} from '@rrweb/types'; import { EventType } from '@rrweb/types'; import { diff --git a/packages/all/test/utils.ts b/packages/all/test/utils.ts index 5f8aaab932..110f8fb662 100644 --- a/packages/all/test/utils.ts +++ b/packages/all/test/utils.ts @@ -1,4 +1,4 @@ -import { NodeType } from 'rrweb-snapshot'; +import { NodeType } from '@saola.ai/rrweb-snapshot'; import { expect } from 'vitest'; import { EventType, diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json index 18b98fcc60..c1a8f97931 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-record/src/index.ts b/packages/plugins/rrweb-plugin-canvas-webrtc-record/src/index.ts index 4bb8f8f65a..87bea2f085 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-record/src/index.ts +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-record/src/index.ts @@ -1,4 +1,4 @@ -import type { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from '@saola.ai/rrweb-snapshot'; import SimplePeer from 'simple-peer-light'; import type { RecordPlugin, ICrossOriginIframeMirror } from '@rrweb/types'; import type { WebRTCDataChannel } from './types'; diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json index 9836dccab6..1683e4fd72 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts index 7f9bb15d87..653c7b8935 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts @@ -1,7 +1,7 @@ import type { RRNode } from 'rrdom'; -import type { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from '@saola.ai/rrweb-snapshot'; import SimplePeer from 'simple-peer-light'; -import type { ReplayPlugin, Replayer } from 'rrweb'; +import type { ReplayPlugin, Replayer } from '@saola.ai/rrweb'; import type { WebRTCDataChannel } from './types'; // TODO: restrict callback to real nodes only, or make sure callback gets called when real node gets added to dom as well diff --git a/packages/plugins/rrweb-plugin-console-record/package.json b/packages/plugins/rrweb-plugin-console-record/package.json index 6f0eb0d532..8777bb3781 100644 --- a/packages/plugins/rrweb-plugin-console-record/package.json +++ b/packages/plugins/rrweb-plugin-console-record/package.json @@ -45,7 +45,7 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1", @@ -53,6 +53,6 @@ "puppeteer": "^20.9.0" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-console-record/src/index.ts b/packages/plugins/rrweb-plugin-console-record/src/index.ts index 8587a36756..7d61c3ac66 100644 --- a/packages/plugins/rrweb-plugin-console-record/src/index.ts +++ b/packages/plugins/rrweb-plugin-console-record/src/index.ts @@ -1,5 +1,5 @@ import type { listenerHandler, RecordPlugin, IWindow } from '@rrweb/types'; -import { utils } from 'rrweb'; +import { utils } from '@saola.ai/rrweb'; import { ErrorStackParser, StackFrame } from './error-stack-parser'; import { stringify } from './stringify'; diff --git a/packages/plugins/rrweb-plugin-console-record/test/html/index.ts b/packages/plugins/rrweb-plugin-console-record/test/html/index.ts index d701b7991e..9a76fd1dcf 100644 --- a/packages/plugins/rrweb-plugin-console-record/test/html/index.ts +++ b/packages/plugins/rrweb-plugin-console-record/test/html/index.ts @@ -1,5 +1,5 @@ import type { eventWithTime } from '@rrweb/types'; -import { record } from 'rrweb'; +import { record } from '@saola.ai/rrweb'; import { getRecordConsolePlugin } from '../../src/index'; window.Date.now = () => new Date(Date.UTC(2018, 10, 15, 8)).valueOf(); diff --git a/packages/plugins/rrweb-plugin-console-replay/package.json b/packages/plugins/rrweb-plugin-console-replay/package.json index c3ba862b24..b770bc8009 100644 --- a/packages/plugins/rrweb-plugin-console-replay/package.json +++ b/packages/plugins/rrweb-plugin-console-replay/package.json @@ -44,12 +44,12 @@ "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { "@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.15", - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-console-replay/src/index.ts b/packages/plugins/rrweb-plugin-console-replay/src/index.ts index 5ed6d2576a..3da7edcf6e 100644 --- a/packages/plugins/rrweb-plugin-console-replay/src/index.ts +++ b/packages/plugins/rrweb-plugin-console-replay/src/index.ts @@ -5,7 +5,7 @@ import { } from '@rrweb/rrweb-plugin-console-record'; import type { eventWithTime } from '@rrweb/types'; import { EventType, IncrementalSource } from '@rrweb/types'; -import type { ReplayPlugin, Replayer } from 'rrweb'; +import type { ReplayPlugin, Replayer } from '@saola.ai/rrweb'; /** * define an interface to replay log records diff --git a/packages/plugins/rrweb-plugin-sequential-id-record/package.json b/packages/plugins/rrweb-plugin-sequential-id-record/package.json index ca3433530f..feb902d434 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-record/package.json +++ b/packages/plugins/rrweb-plugin-sequential-id-record/package.json @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-sequential-id-replay/package.json b/packages/plugins/rrweb-plugin-sequential-id-replay/package.json index a83b0d4172..9c6c979e61 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-replay/package.json +++ b/packages/plugins/rrweb-plugin-sequential-id-replay/package.json @@ -44,12 +44,12 @@ "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { "@rrweb/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.15", - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "typescript": "^4.7.3", "vite": "^5.2.8", "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" } } diff --git a/packages/plugins/rrweb-plugin-sequential-id-replay/src/index.ts b/packages/plugins/rrweb-plugin-sequential-id-replay/src/index.ts index 189afb43fd..3140064c54 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-replay/src/index.ts +++ b/packages/plugins/rrweb-plugin-sequential-id-replay/src/index.ts @@ -1,5 +1,5 @@ import type { SequentialIdOptions } from '@rrweb/rrweb-plugin-sequential-id-record'; -import type { ReplayPlugin } from 'rrweb'; +import type { ReplayPlugin } from '@saola.ai/rrweb'; import type { eventWithTime } from '@rrweb/types'; type Options = SequentialIdOptions & { diff --git a/packages/record/package.json b/packages/record/package.json index 4e973a942c..5f27d3efec 100644 --- a/packages/record/package.json +++ b/packages/record/package.json @@ -56,7 +56,7 @@ }, "dependencies": { "@rrweb/types": "^2.0.0-alpha.15", - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/record/src/index.ts b/packages/record/src/index.ts index c2820aa1d2..860006ad75 100644 --- a/packages/record/src/index.ts +++ b/packages/record/src/index.ts @@ -1,3 +1,3 @@ -import { record } from 'rrweb'; +import { record } from '@saola.ai/rrweb'; export { record }; diff --git a/packages/replay/package.json b/packages/replay/package.json index d0329ec93a..97992bb506 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@rrweb/types": "^2.0.0-alpha.15", - "rrweb": "^2.0.0-alpha.15" + "@saola.ai/rrweb": "^2.0.0-alpha.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/replay/src/index.ts b/packages/replay/src/index.ts index 1df4de17e1..51cbb4b7db 100644 --- a/packages/replay/src/index.ts +++ b/packages/replay/src/index.ts @@ -3,7 +3,7 @@ import { type playerConfig, type PlayerMachineState, type SpeedMachineState, -} from 'rrweb'; -import 'rrweb/dist/style.css'; +} from '@saola.ai/rrweb'; +import '@saola.ai/rrweb/dist/style.css'; export { Replayer, playerConfig, PlayerMachineState, SpeedMachineState }; diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index e3feff897e..4d7d37882c 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -56,7 +56,7 @@ "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", "rrdom": "^2.0.0-alpha.15", - "rrweb-snapshot": "^2.0.0-alpha.15" + "@saola.ai/rrweb-snapshot": "^2.0.0-alpha.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index 6d47d3560a..fb362addf7 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@saola.ai/rrweb-snapshot'; import type { NWSAPI } from 'nwsapi'; import type { CSSStyleDeclaration as CSSStyleDeclarationType } from 'cssstyle'; import { diff --git a/packages/rrdom-nodejs/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts index b6be55bc0f..254336a909 100644 --- a/packages/rrdom-nodejs/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeAll } from 'vitest'; import * as fs from 'fs'; import * as path from 'path'; -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@saola.ai/rrweb-snapshot'; import { RRCanvasElement, RRCDATASection, diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 6662fadb38..140897cc94 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -52,6 +52,6 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.15" + "@saola.ai/rrweb-snapshot": "^2.0.0-alpha.15" } } diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index d5093b38fb..666cb25d14 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -2,7 +2,7 @@ import { NodeType as RRNodeType, Mirror as NodeMirror, elementNode, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { canvasMutationData, canvasEventWithTime, diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index 70450123f1..c82c56cea0 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -1,4 +1,4 @@ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@saola.ai/rrweb-snapshot'; import { parseCSSText, camelize, toCSSText } from './style'; export interface IRRNode { parentElement: IRRNode | null; diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index 65ae197de8..dadd8b934b 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -1,12 +1,12 @@ import { NodeType as RRNodeType, createMirror as createNodeMirror, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { Mirror as NodeMirror, IMirror, serializedNodeWithId, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { canvasMutationData, canvasEventWithTime, diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index ff817a1627..8e80f5597a 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -10,7 +10,7 @@ import { createMirror, Mirror as NodeMirror, serializedNodeWithId, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import { buildFromDom, getDefaultSN, diff --git a/packages/rrdom/test/document.test.ts b/packages/rrdom/test/document.test.ts index fb43b0f6ab..eab767a62f 100644 --- a/packages/rrdom/test/document.test.ts +++ b/packages/rrdom/test/document.test.ts @@ -1,7 +1,7 @@ /** * @jest-environment jsdom */ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@saola.ai/rrweb-snapshot'; import { BaseRRDocument, BaseRRDocumentType, diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index e3b4d11efc..d191fdda3f 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -16,7 +16,7 @@ import { NodeType, NodeType as RRNodeType, textNode, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import { buildFromDom, buildFromNode, diff --git a/packages/rrweb-player/src/types.ts b/packages/rrweb-player/src/types.ts index 74c6bb280a..2bde63abc4 100644 --- a/packages/rrweb-player/src/types.ts +++ b/packages/rrweb-player/src/types.ts @@ -1,6 +1,6 @@ import type { eventWithTime } from '@rrweb/types'; import type { Replayer, playerConfig } from '@rrweb/replay'; -import type { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from '@saola.ai/rrweb-snapshot'; export type RRwebPlayerOptions = { target: HTMLElement; diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index aeb15260d4..67bcc8ead9 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,5 +1,5 @@ { - "name": "rrweb-snapshot", + "name": "@saola.ai/rrweb-snapshot", "version": "2.0.0-alpha.15", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts index e2b4fc20c7..2b09602ae4 100644 --- a/packages/rrweb-snapshot/test/utils.test.ts +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -4,7 +4,7 @@ import { describe, it, test, expect } from 'vitest'; import { NodeType, serializedNode } from '../src/types'; import { extractFileExtension, isNodeMetaEqual } from '../src/utils'; -import type { serializedNodeWithId } from 'rrweb-snapshot'; +import type { serializedNodeWithId } from '@saola.ai/rrweb-snapshot'; describe('utils', () => { describe('isNodeMetaEqual()', () => { diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 063b1e3eed..db5b5d4368 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,5 +1,5 @@ { - "name": "rrweb", + "name": "@saola.ai/rrweb", "version": "2.0.0-alpha.15", "description": "record and replay the web", "scripts": { @@ -86,6 +86,6 @@ "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", "rrdom": "^2.0.0-alpha.15", - "rrweb-snapshot": "^2.0.0-alpha.15" + "@saola.ai/rrweb-snapshot": "^2.0.0-alpha.15" } } diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 20553feaf4..a75f439681 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -1,5 +1,5 @@ -import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import { genId, NodeType } from 'rrweb-snapshot'; +import type { Mirror, serializedNodeWithId } from '@saola.ai/rrweb-snapshot'; +import { genId, NodeType } from '@saola.ai/rrweb-snapshot'; import type { CrossOriginIframeMessageEvent } from '../types'; import CrossOriginIframeMirror from './cross-origin-iframe-mirror'; import { EventType, IncrementalSource } from '@rrweb/types'; diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index ffc6f88b6d..f397494353 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -3,7 +3,7 @@ import { MaskInputOptions, SlimDOMOptions, createMirror, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import { initObservers, mutationBuffers } from './observer'; import { on, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index a798441969..5e60c35bbb 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -10,7 +10,7 @@ import { isNativeShadowDom, getInputType, toLowerCase, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { observerParam, MutationBufferParam } from '../types'; import type { mutationRecord, diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index dcd30e4718..a29ad8aca5 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -4,7 +4,7 @@ import { Mirror, getInputType, toLowerCase, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { FontFaceSet } from 'css-font-loading-module'; import { throttle, diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index e40935f574..1e04ee89db 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -1,4 +1,4 @@ -import type { ICanvas, Mirror, DataURLOptions } from 'rrweb-snapshot'; +import type { ICanvas, Mirror, DataURLOptions } from '@saola.ai/rrweb-snapshot'; import type { blockClass, canvasManagerMutationCallback, diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index 4f6b30fc36..608339afc8 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -1,4 +1,4 @@ -import type { ICanvas } from 'rrweb-snapshot'; +import type { ICanvas } from '@saola.ai/rrweb-snapshot'; import type { blockClass, IWindow, listenerHandler } from '@rrweb/types'; import { isBlocked, patch } from '../../../utils'; diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index 00161b8ffd..afd91a3dd9 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -1,4 +1,4 @@ -import type { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from '@saola.ai/rrweb-snapshot'; import { blockClass, CanvasContext, diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index 169c77216a..457dbe91d8 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -10,8 +10,8 @@ import { initAdoptedStyleSheetObserver, } from './observer'; import { patch, inDom } from '../utils'; -import type { Mirror } from 'rrweb-snapshot'; -import { isNativeShadowDom } from 'rrweb-snapshot'; +import type { Mirror } from '@saola.ai/rrweb-snapshot'; +import { isNativeShadowDom } from '@saola.ai/rrweb-snapshot'; type BypassOptions = Omit< MutationBufferParam, diff --git a/packages/rrweb/src/record/stylesheet-manager.ts b/packages/rrweb/src/record/stylesheet-manager.ts index 5395cde15d..89063450ef 100644 --- a/packages/rrweb/src/record/stylesheet-manager.ts +++ b/packages/rrweb/src/record/stylesheet-manager.ts @@ -1,5 +1,5 @@ -import type { elementNode, serializedNodeWithId } from 'rrweb-snapshot'; -import { stringifyRule } from 'rrweb-snapshot'; +import type { elementNode, serializedNodeWithId } from '@saola.ai/rrweb-snapshot'; +import { stringifyRule } from '@saola.ai/rrweb-snapshot'; import type { adoptedStyleSheetCallback, adoptedStyleSheetParam, diff --git a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts index 374edfe1b0..8de3cfc582 100644 --- a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts +++ b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts @@ -1,5 +1,5 @@ import { encode } from 'base64-arraybuffer'; -import type { DataURLOptions } from 'rrweb-snapshot'; +import type { DataURLOptions } from '@saola.ai/rrweb-snapshot'; import type { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse, diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index a86f4aac81..11ae896a9b 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -9,7 +9,7 @@ import { attributes, serializedElementNodeWithId, toLowerCase, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import { RRDocument, createOrGetNode, diff --git a/packages/rrweb/src/replay/media/index.ts b/packages/rrweb/src/replay/media/index.ts index 9f9a722954..b081290ba9 100644 --- a/packages/rrweb/src/replay/media/index.ts +++ b/packages/rrweb/src/replay/media/index.ts @@ -1,7 +1,7 @@ import { Emitter, MediaInteractions, ReplayerEvents } from '@rrweb/types'; import type { RRMediaElement } from 'rrdom'; import type { createPlayerService, createSpeedService } from '../machine'; -import type { Mirror, mediaAttributes } from 'rrweb-snapshot'; +import type { Mirror, mediaAttributes } from '@saola.ai/rrweb-snapshot'; import type { mediaInteractionData } from '@rrweb/types'; type MediaState = { diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 75be0fc629..ce177bc598 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -5,7 +5,7 @@ import type { MaskInputFn, MaskTextFn, DataURLOptions, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; import type { IframeManager } from './record/iframe-manager'; import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index b7fa12506e..b993b0d99d 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -9,8 +9,8 @@ import type { DeprecatedMirror, textMutation, } from '@rrweb/types'; -import type { IMirror, Mirror, SlimDOMOptions } from 'rrweb-snapshot'; -import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot'; +import type { IMirror, Mirror, SlimDOMOptions } from '@saola.ai/rrweb-snapshot'; +import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from '@saola.ai/rrweb-snapshot'; import type { RRNode, RRIFrameElement } from 'rrdom'; export function on( diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 8693689256..3480740974 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -15,7 +15,7 @@ import { } from './utils'; import type { recordOptions } from '../src/types'; import { eventWithTime, EventType, RecordPlugin } from '@rrweb/types'; -import { visitSnapshot, NodeType } from 'rrweb-snapshot'; +import { visitSnapshot, NodeType } from '@saola.ai/rrweb-snapshot'; describe('record integration tests', function (this: ISuite) { vi.setConfig({ testTimeout: 10_000 }); diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index 32a3ee8197..453292caa1 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -16,7 +16,7 @@ import { stripBase64, waitForRAF, } from '../utils'; -import type { ICanvas } from 'rrweb-snapshot'; +import type { ICanvas } from '@saola.ai/rrweb-snapshot'; interface ISuite { code: string; diff --git a/packages/rrweb/test/rrdom.test.ts b/packages/rrweb/test/rrdom.test.ts index 7ef0866aa6..becd7471d9 100644 --- a/packages/rrweb/test/rrdom.test.ts +++ b/packages/rrweb/test/rrdom.test.ts @@ -4,7 +4,7 @@ import { EventType, IncrementalSource, Replayer, eventWithTime } from '../src'; import { vi, type MockInstance } from 'vitest'; import type { styleDeclarationData, styleSheetRuleData } from '@rrweb/types'; -import { createMirror, Mirror as NodeMirror } from 'rrweb-snapshot'; +import { createMirror, Mirror as NodeMirror } from '@saola.ai/rrweb-snapshot'; import type { ReplayerHandler } from 'rrdom'; describe('diff algorithm for rrdom', () => { diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 8348eb0f7a..c75379d14c 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -1,4 +1,4 @@ -import { NodeType } from 'rrweb-snapshot'; +import { NodeType } from '@saola.ai/rrweb-snapshot'; import { EventType, IncrementalSource, diff --git a/packages/types/package.json b/packages/types/package.json index 8f14d19ebf..d0dd810ee2 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -50,7 +50,7 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.15" + "@saola.ai/rrweb-snapshot": "^2.0.0-alpha.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 75155cab34..6f96314b19 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -3,7 +3,7 @@ import type { Mirror, INode, DataURLOptions, -} from 'rrweb-snapshot'; +} from '@saola.ai/rrweb-snapshot'; export enum EventType { DomContentLoaded, diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 4b398b5562..9b1dc0f79d 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -42,7 +42,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.15", + "@saola.ai/rrweb": "^2.0.0-alpha.15", "rrweb-player": "^2.0.0-alpha.15" } } diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts index 4f258239e2..78736e3b8f 100644 --- a/packages/web-extension/src/content/inject.ts +++ b/packages/web-extension/src/content/inject.ts @@ -1,5 +1,5 @@ -import { record } from 'rrweb'; -import type { recordOptions } from 'rrweb'; +import { record } from '@saola.ai/rrweb'; +import type { recordOptions } from '@saola.ai/rrweb'; import type { eventWithTime } from '@rrweb/types'; import { MessageName, RecordStartedMessage } from '~/types'; import { isInCrossOriginIFrame } from '~/utils'; From 30c3a75e6b32dfc4c680b84e6e1c1aa342802634 Mon Sep 17 00:00:00 2001 From: reemhagbi Date: Sun, 16 Jun 2024 13:57:09 +0300 Subject: [PATCH 02/34] rrdom + packer + web-extension --- packages/all/package.json | 2 +- packages/all/src/index.ts | 2 +- packages/all/test/cross-origin-iframe-packer.test.ts | 2 +- packages/packer/package.json | 3 +-- .../plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts | 2 +- packages/rrdom-nodejs/package.json | 2 +- packages/rrdom-nodejs/src/document-nodejs.ts | 2 +- packages/rrdom-nodejs/src/polyfill.ts | 2 +- packages/rrdom-nodejs/test/document-nodejs.test.ts | 2 +- packages/rrdom-nodejs/test/polyfill.test.ts | 2 +- packages/rrdom/package.json | 2 +- packages/rrvideo/package.json | 2 +- packages/rrweb-player/package.json | 2 +- packages/rrweb-player/src/Player.svelte | 2 +- packages/rrweb/package.json | 2 +- packages/rrweb/src/replay/index.ts | 4 ++-- packages/rrweb/src/replay/media/index.ts | 2 +- packages/rrweb/src/types.ts | 2 +- packages/rrweb/src/utils.ts | 2 +- packages/rrweb/test/rrdom.test.ts | 2 +- packages/types/src/index.ts | 2 +- packages/web-extension/package.json | 2 +- 22 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/all/package.json b/packages/all/package.json index 880d972993..d464db91da 100644 --- a/packages/all/package.json +++ b/packages/all/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@rrweb/types": "^2.0.0-alpha.15", - "@rrweb/packer": "^2.0.0-alpha.15", + "@saola.ai/rrweb-packer": "^2.0.0-alpha.15", "@saola.ai/rrweb": "^2.0.0-alpha.15" }, "browserslist": [ diff --git a/packages/all/src/index.ts b/packages/all/src/index.ts index 291c6caff3..e4f5c727e9 100644 --- a/packages/all/src/index.ts +++ b/packages/all/src/index.ts @@ -1,4 +1,4 @@ export * from '@saola.ai/rrweb'; -export * from '@rrweb/packer'; +export * from '@saola.ai/rrweb-packer'; // export * from '@rrweb/rrweb-plugin-console-record'; // export * from '@rrweb/rrweb-plugin-console-replay'; diff --git a/packages/all/test/cross-origin-iframe-packer.test.ts b/packages/all/test/cross-origin-iframe-packer.test.ts index 2bcaf266f4..f09d3a04af 100644 --- a/packages/all/test/cross-origin-iframe-packer.test.ts +++ b/packages/all/test/cross-origin-iframe-packer.test.ts @@ -12,7 +12,7 @@ import type { listenerHandler, mutationData, } from '@rrweb/types'; -import { unpack } from '@rrweb/packer'; +import { unpack } from '@saola.ai/rrweb-packer'; import * as fs from 'fs'; import * as path from 'path'; import type * as puppeteer from 'puppeteer'; diff --git a/packages/packer/package.json b/packages/packer/package.json index d984e78855..6763ed44c0 100644 --- a/packages/packer/package.json +++ b/packages/packer/package.json @@ -1,5 +1,5 @@ { - "name": "@rrweb/packer", + "name": "@saola.ai/rrweb-packer", "version": "2.0.0-alpha.15", "publishConfig": { "access": "public" @@ -17,7 +17,6 @@ "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/packer#readme", "bugs": { "url": "https://github.com/rrweb-io/rrweb/issues" }, diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts index 653c7b8935..10b2ac84ec 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/src/index.ts @@ -1,4 +1,4 @@ -import type { RRNode } from 'rrdom'; +import type { RRNode } from '@saola.ai/rrdom'; import type { Mirror } from '@saola.ai/rrweb-snapshot'; import SimplePeer from 'simple-peer-light'; import type { ReplayPlugin, Replayer } from '@saola.ai/rrweb'; diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 4d7d37882c..a6be46d0b3 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -55,7 +55,7 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^2.0.0-alpha.15", + "@saola.ai/rrdom": "^2.0.0-alpha.15", "@saola.ai/rrweb-snapshot": "^2.0.0-alpha.15" }, "browserslist": [ diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index fb362addf7..7821cd4188 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -14,7 +14,7 @@ import { ClassList, IRRDocument, CSSStyleDeclaration, -} from 'rrdom'; +} from '@saola.ai/rrdom'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires const nwsapi = require('nwsapi'); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires diff --git a/packages/rrdom-nodejs/src/polyfill.ts b/packages/rrdom-nodejs/src/polyfill.ts index 19674611c6..dc178e4c0e 100644 --- a/packages/rrdom-nodejs/src/polyfill.ts +++ b/packages/rrdom-nodejs/src/polyfill.ts @@ -1,4 +1,4 @@ -import { BaseRRNode } from 'rrdom'; +import { BaseRRNode } from '@saola.ai/rrdom'; import { RRDocument } from './document-nodejs'; /** diff --git a/packages/rrdom-nodejs/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts index 254336a909..52ed952877 100644 --- a/packages/rrdom-nodejs/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -17,7 +17,7 @@ import { RRStyleElement, RRText, } from '../src/document-nodejs'; -import { buildFromDom } from 'rrdom'; +import { buildFromDom } from '@saola.ai/rrdom'; describe('RRDocument for nodejs environment', () => { describe('RRDocument API', () => { diff --git a/packages/rrdom-nodejs/test/polyfill.test.ts b/packages/rrdom-nodejs/test/polyfill.test.ts index 0e7f8cbcda..6b933f444f 100644 --- a/packages/rrdom-nodejs/test/polyfill.test.ts +++ b/packages/rrdom-nodejs/test/polyfill.test.ts @@ -9,7 +9,7 @@ import { polyfillDocument, } from '../src/polyfill'; import { performance as nativePerformance } from 'perf_hooks'; -import { BaseRRNode } from 'rrdom'; +import { BaseRRNode } from '@saola.ai/rrdom'; describe('polyfill for nodejs', () => { it('should polyfill performance api', () => { diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 140897cc94..219159328f 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,5 +1,5 @@ { - "name": "rrdom", + "name": "@saola.ai/rrdom", "version": "2.0.0-alpha.15", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index b97873039e..de013d649a 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,5 +1,5 @@ { - "name": "rrvideo", + "name": "@saola.ai/rrvideo", "version": "2.0.0-alpha.15", "description": "transform rrweb session into video", "main": "build/index.js", diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 98e8a9cd1f..1f009207a4 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -21,7 +21,7 @@ "dependencies": { "@tsconfig/svelte": "^1.0.0", "@rrweb/replay": "^2.0.0-alpha.15", - "@rrweb/packer": "^2.0.0-alpha.15" + "@saola.ai/rrweb-packer": "^2.0.0-alpha.15" }, "scripts": { "dev": "vite build --watch", diff --git a/packages/rrweb-player/src/Player.svelte b/packages/rrweb-player/src/Player.svelte index 242c4f6562..6eb0c75876 100644 --- a/packages/rrweb-player/src/Player.svelte +++ b/packages/rrweb-player/src/Player.svelte @@ -1,7 +1,7 @@ +
hover me
" @@ -487,7 +613,7 @@ exports[`integration tests > [html file]: with-style-sheet.html 1`] = ` with style sheet - + " `; @@ -498,11 +624,224 @@ exports[`integration tests > [html file]: with-style-sheet-with-import.html 1`] with style sheet with import - + + " `; +exports[`integration tests > should be able to record elements even when .childNodes has been monkey patched 1`] = ` +"{ + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Document\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 14 + } + ], + \\"id\\": 13 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 16 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"ul\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"li\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"a\\", + \\"id\\": 22 + } + ], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"li\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"b\\", + \\"id\\": 25 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 26 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"li\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"c\\", + \\"id\\": 28 + } + ], + \\"id\\": 27 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"li\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"d\\", + \\"id\\": 31 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 32 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 33 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 +}" +`; + exports[`shadow DOM integration tests > snapshot shadow DOM 1`] = ` "{ \\"type\\": 0, @@ -755,12 +1094,13 @@ exports[`shadow DOM integration tests > snapshot shadow DOM 1`] = ` { \\"type\\": 2, \\"tagName\\": \\"style\\", - \\"attributes\\": {}, + \\"attributes\\": { + \\"_cssText\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px / 22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\" + }, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px / 22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\", - \\"isStyle\\": true, + \\"textContent\\": \\"\\", \\"id\\": 38 } ], diff --git a/packages/rrweb-snapshot/test/alt-css/alt-style.css b/packages/rrweb-snapshot/test/alt-css/alt-style.css new file mode 100644 index 0000000000..bda88053d8 --- /dev/null +++ b/packages/rrweb-snapshot/test/alt-css/alt-style.css @@ -0,0 +1,12 @@ +body { + margin: 0; + background: url('../should-be-in-root-folder.jpg'); + border-image: url('data:image/svg+xml;utf8,'); + } + p { + color: red; + background: url('./should-be-in-alt-css-folder.jpg'); + } + body > p { + color: yellow; + } diff --git a/packages/rrweb-snapshot/test/css.test.ts b/packages/rrweb-snapshot/test/css.test.ts index fc8e5a2946..75e261c102 100644 --- a/packages/rrweb-snapshot/test/css.test.ts +++ b/packages/rrweb-snapshot/test/css.test.ts @@ -1,255 +1,250 @@ -import { describe, it, expect } from 'vitest'; -import { parse, Rule, Media } from '../src/css'; -import { fixSafariColons, escapeImportStatement } from './../src/utils'; +/** + * @vitest-environment jsdom + */ +import { describe, it, beforeEach, expect } from 'vitest'; +import { mediaSelectorPlugin, pseudoClassPlugin } from '../src/css'; +import postcss, { type AcceptedPlugin } from 'postcss'; +import { JSDOM } from 'jsdom'; +import { splitCssText, stringifyStylesheet } from './../src/utils'; +import { applyCssSplits } from './../src/rebuild'; +import { + NodeType, + type serializedElementNodeWithId, + type BuildCache, + type textNode, +} from '../src/types'; +import { Window } from 'happy-dom'; describe('css parser', () => { - it('should save the filename and source', () => { - const css = 'booty {\n size: large;\n}\n'; - const ast = parse(css, { - source: 'booty.css', + function parse(plugin: AcceptedPlugin, input: string): string { + const ast = postcss([plugin]).process(input, {}); + return ast.css; + } + + describe('mediaSelectorPlugin', () => { + it('selectors without device remain unchanged', () => { + const cssText = + '@media only screen and (min-width: 1200px) { .a { width: 10px; }}'; + expect(parse(mediaSelectorPlugin, cssText)).toEqual(cssText); }); - expect(ast.stylesheet!.source).toEqual('booty.css'); - - const position = ast.stylesheet!.rules[0].position!; - expect(position.start).toBeTruthy(); - expect(position.end).toBeTruthy(); - expect(position.source).toEqual('booty.css'); - expect(position.content).toEqual(css); + it('can adapt media rules to replay context', () => { + [ + ['min', 'width'], + ['min', 'height'], + ['max', 'width'], + ['max', 'height'], + ].forEach(([first, second]) => { + expect( + parse( + mediaSelectorPlugin, + `@media only screen and (${first}-device-${second}: 1200px) { .a { width: 10px; }}`, + ), + ).toEqual( + `@media only screen and (${first}-${second}: 1200px) { .a { width: 10px; }}`, + ); + }); + }); }); - it('should throw when a selector is missing', () => { - expect(() => { - parse('{size: large}'); - }).toThrow(); + describe('pseudoClassPlugin', () => { + it('parses nested commas in selectors correctly', () => { + const cssText = + 'body > ul :is(li:not(:first-of-type) a.current, li:not(:first-of-type).active a) {background: red;}'; + expect(parse(pseudoClassPlugin, cssText)).toEqual(cssText); + }); - expect(() => { - parse('b { color: red; }\n{ color: green; }\na { color: blue; }'); - }).toThrow(); - }); + it("doesn't ignore :hover within :is brackets", () => { + const cssText = + 'body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a) {background: red;}'; + expect(parse(pseudoClassPlugin, cssText)) + .toEqual(`body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a), +body > ul :is(li:not(:first-of-type) a.\\:hover, li:not(:first-of-type).active a) {background: red;}`); + }); - it('should throw when a broken comment is found', () => { - expect(() => { - parse('thing { color: red; } /* b { color: blue; }'); - }).toThrow(); + it('should parse selector with comma nested inside ()', () => { + const cssText = + '[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }'; + expect(parse(pseudoClassPlugin, cssText)) + .toEqual(`[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active), +[_nghost-ng-c4172599085]:not(.fit-content).aim-select.\\:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }`); + }); - expect(() => { - parse('/*'); - }).toThrow(); + it('ignores ( in strings', () => { + const cssText = + 'li[attr="weirdly("] a:hover, li[attr="weirdly)"] a {background-color: red;}'; + expect(parse(pseudoClassPlugin, cssText)) + .toEqual(`li[attr="weirdly("] a:hover, li[attr="weirdly)"] a, +li[attr="weirdly("] a.\\:hover {background-color: red;}`); + }); - /* Nested comments should be fine */ - expect(() => { - parse('/* /* */'); - }).not.toThrow(); - }); + it('ignores escaping in strings', () => { + const cssText = `li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a {background-color: red;}`; + expect(parse(pseudoClassPlugin, cssText)) + .toEqual(`li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a, +li[attr="weirder\\"("] a.\\:hover {background-color: red;}`); + }); - it('should allow empty property value', () => { - expect(() => { - parse('p { color:; }'); - }).not.toThrow(); + it('ignores comma in string', () => { + const cssText = 'li[attr="has,comma"] a:hover {background: red;}'; + expect(parse(pseudoClassPlugin, cssText)).toEqual( + `li[attr="has,comma"] a:hover, +li[attr="has,comma"] a.\\:hover {background: red;}`, + ); + }); }); +}); - it('should not throw with silent option', () => { - expect(() => { - parse('thing { color: red; } /* b { color: blue; }', { silent: true }); - }).not.toThrow(); +describe('css splitter', () => { + it('finds css textElement splits correctly', () => { + const window = new Window({ url: 'https://localhost:8080' }); + const document = window.document; + document.head.innerHTML = ''; + const style = document.querySelector('style'); + if (style) { + // as authored, e.g. no spaces + style.append('.a{background-color:black;}'); + + // how it is currently stringified (spaces present) + const expected = [ + '.a { background-color: red; }', + '.a { background-color: black; }', + ]; + const browserSheet = expected.join(''); + expect(stringifyStylesheet(style.sheet!)).toEqual(browserSheet); + + expect(splitCssText(browserSheet, style)).toEqual(expected); + } }); - it('should list the parsing errors and continue parsing', () => { - const result = parse( - 'foo { color= red; } bar { color: blue; } baz {}} boo { display: none}', - { - silent: true, - source: 'foo.css', - }, - ); - - const rules = result.stylesheet!.rules; - expect(rules.length).toBeGreaterThan(2); - - const errors = result.stylesheet!.parsingErrors!; - expect(errors.length).toEqual(2); - - expect(errors[0]).toHaveProperty('message'); - expect(errors[0]).toHaveProperty('reason'); - expect(errors[0]).toHaveProperty('filename'); - expect(errors[0]).toHaveProperty('line'); - expect(errors[0]).toHaveProperty('column'); - expect(errors[0]).toHaveProperty('source'); - expect(errors[0].filename).toEqual('foo.css'); + it('finds css textElement splits correctly when comments are present', () => { + const window = new Window({ url: 'https://localhost:8080' }); + const document = window.document; + // as authored, with comment, missing semicolons + document.head.innerHTML = + ''; + const style = document.querySelector('style'); + if (style) { + style.append('/* author comment */.a{color:red}.b{color:green}'); + + // how it is currently stringified (spaces present) + const expected = [ + '.a { color: red; } .b { color: blue; }', + '.a { color: red; } .b { color: green; }', + ]; + const browserSheet = expected.join(''); + expect(splitCssText(browserSheet, style)).toEqual(expected); + } }); - it('should parse selector with comma nested inside ()', () => { - const result = parse( - '[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }', - ); - - expect(result.parent).toEqual(null); - - const rules = result.stylesheet!.rules; - expect(rules.length).toEqual(1); - - let rule = rules[0] as Rule; - expect(rule.parent).toEqual(result); - expect(rule.selectors?.length).toEqual(1); - - let decl = rule.declarations![0]; - expect(decl.parent).toEqual(rule); + it('finds css textElement splits correctly when vendor prefixed rules have been removed', () => { + const style = JSDOM.fragment(``).querySelector('style'); + if (style) { + // as authored, with newlines + style.appendChild( + JSDOM.fragment(`.x { + -webkit-transition: all 4s ease; + content: 'try to keep a newline'; + transition: all 4s ease; +}`), + ); + // TODO: splitCssText can't handle it yet if both start with .x + style.appendChild( + JSDOM.fragment(`.y { + -moz-transition: all 5s ease; + transition: all 5s ease; +}`), + ); + // browser .rules would usually omit the vendored versions and modifies the transition value + const expected = [ + '.x { content: "try to keep a newline"; background: red; transition: 4s; }', + '.y { transition: 5s; }', + ]; + const browserSheet = expected.join(''); + + // can't do this as JSDOM doesn't have style.sheet + // also happy-dom doesn't strip out vendor-prefixed rules like a real browser does + //expect(stringifyStylesheet(style.sheet!)).toEqual(browserSheet); + + expect(splitCssText(browserSheet, style)).toEqual(expected); + } }); +}); - it('parses { and } in attribute selectors correctly', () => { - const result = parse('foo[someAttr~="{someId}"] { color: red; }'); - const rules = result.stylesheet!.rules; - - expect(rules.length).toEqual(1); - - const rule = rules[0] as Rule; - - expect(rule.selectors![0]).toEqual('foo[someAttr~="{someId}"]'); +describe('applyCssSplits css rejoiner', function () { + const mockLastUnusedArg = null as unknown as BuildCache; + const halfCssText = '.a { background-color: red; }'; + const otherHalfCssText = halfCssText.replace('.a', '.x'); + const markedCssText = [halfCssText, otherHalfCssText].join('/* rr_split */'); + let sn: serializedElementNodeWithId; + + beforeEach(() => { + sn = { + type: NodeType.Element, + tagName: 'style', + childNodes: [ + { + type: NodeType.Text, + textContent: '', + }, + { + type: NodeType.Text, + textContent: '', + }, + ], + } as serializedElementNodeWithId; }); - it('should set parent property', () => { - const result = parse( - 'thing { test: value; }\n' + - '@media (min-width: 100px) { thing { test: value; } }', + it('applies css splits correctly', () => { + // happy path + applyCssSplits(sn, markedCssText, false, mockLastUnusedArg); + expect((sn.childNodes[0] as textNode).textContent).toEqual(halfCssText); + expect((sn.childNodes[1] as textNode).textContent).toEqual( + otherHalfCssText, ); - - expect(result.parent).toEqual(null); - - const rules = result.stylesheet!.rules; - expect(rules.length).toEqual(2); - - let rule = rules[0] as Rule; - expect(rule.parent).toEqual(result); - expect(rule.declarations!.length).toEqual(1); - - let decl = rule.declarations![0]; - expect(decl.parent).toEqual(rule); - - const media = rules[1] as Media; - expect(media.parent).toEqual(result); - expect(media.rules!.length).toEqual(1); - - rule = media.rules![0] as Rule; - expect(rule.parent).toEqual(media); - - expect(rule.declarations!.length).toEqual(1); - decl = rule.declarations![0]; - expect(decl.parent).toEqual(rule); - }); - - it('parses : in attribute selectors correctly', () => { - const out1 = fixSafariColons('[data-foo] { color: red; }'); - expect(out1).toEqual('[data-foo] { color: red; }'); - - const out2 = fixSafariColons('[data-foo:other] { color: red; }'); - expect(out2).toEqual('[data-foo\\:other] { color: red; }'); - - const out3 = fixSafariColons('[data-aa\\:other] { color: red; }'); - expect(out3).toEqual('[data-aa\\:other] { color: red; }'); }); - it('parses nested commas in selectors correctly', () => { - const result = parse( - ` -body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a) { - background: red; -} -`, + it('applies css splits correctly even when there are too many child nodes', () => { + let sn3 = { + type: NodeType.Element, + tagName: 'style', + childNodes: [ + { + type: NodeType.Text, + textContent: '', + }, + { + type: NodeType.Text, + textContent: '', + }, + { + type: NodeType.Text, + textContent: '', + }, + ], + } as serializedElementNodeWithId; + applyCssSplits(sn3, markedCssText, false, mockLastUnusedArg); + expect((sn3.childNodes[0] as textNode).textContent).toEqual(halfCssText); + expect((sn3.childNodes[1] as textNode).textContent).toEqual( + otherHalfCssText, ); - expect((result.stylesheet!.rules[0] as Rule)!.selectors!.length).toEqual(1); - - const trickresult = parse( - ` -li[attr="weirdly("] a:hover, li[attr="weirdly)"] a { - background-color: red; -} -`, - ); - expect( - (trickresult.stylesheet!.rules[0] as Rule)!.selectors!.length, - ).toEqual(2); - - const weirderresult = parse( - ` -li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a { - background-color: red; -} -`, - ); - expect( - (weirderresult.stylesheet!.rules[0] as Rule)!.selectors!.length, - ).toEqual(2); - - const commainstrresult = parse( - ` -li[attr="has,comma"] a:hover { - background-color: red; -} -`, - ); - expect( - (commainstrresult.stylesheet!.rules[0] as Rule)!.selectors!.length, - ).toEqual(1); + expect((sn3.childNodes[2] as textNode).textContent).toEqual(''); }); - it('parses imports with quotes correctly', () => { - const out1 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"");`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: null, - supportsText: null, - } as unknown as CSSImportRule); - expect(out1).toEqual(`@import url("/foo.css;900;800\\"");`); - - const out2 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") supports(display: flex);`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: null, - supportsText: 'display: flex', - } as unknown as CSSImportRule); - expect(out2).toEqual( - `@import url("/foo.css;900;800\\"") supports(display: flex);`, + it('maintains entire css text when there are too few child nodes', () => { + let sn1 = { + type: NodeType.Element, + tagName: 'style', + childNodes: [ + { + type: NodeType.Text, + textContent: '', + }, + ], + } as serializedElementNodeWithId; + applyCssSplits(sn1, markedCssText, false, mockLastUnusedArg); + expect((sn1.childNodes[0] as textNode).textContent).toEqual( + halfCssText + otherHalfCssText, ); - - const out3 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"");`, - href: '/foo.css;900;800"', - media: { - length: 1, - mediaText: 'print, screen', - }, - layerName: null, - supportsText: null, - } as unknown as CSSImportRule); - expect(out3).toEqual(`@import url("/foo.css;900;800\\"") print, screen;`); - - const out4 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") layer(layer-1);`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: 'layer-1', - supportsText: null, - } as unknown as CSSImportRule); - expect(out4).toEqual(`@import url("/foo.css;900;800\\"") layer(layer-1);`); - - const out5 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") layer;`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: '', - supportsText: null, - } as unknown as CSSImportRule); - expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`); }); }); diff --git a/packages/rrweb-snapshot/test/css/style-with-import.css b/packages/rrweb-snapshot/test/css/style-with-import.css index 5fa59d8039..a24d901947 100644 --- a/packages/rrweb-snapshot/test/css/style-with-import.css +++ b/packages/rrweb-snapshot/test/css/style-with-import.css @@ -1,2 +1,3 @@ @import '//fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&family=Roboto:wght@100;300;400;500;700&display=swap"'; @import './style.css'; +@import '../alt-css/alt-style.css'; diff --git a/packages/rrweb-snapshot/test/css/style.css b/packages/rrweb-snapshot/test/css/style.css index 2b3faf2a77..29b1da8ec8 100644 --- a/packages/rrweb-snapshot/test/css/style.css +++ b/packages/rrweb-snapshot/test/css/style.css @@ -1,11 +1,11 @@ body { margin: 0; - background: url('../a.jpg'); + background: url('../should-be-in-root-folder.jpg'); border-image: url('data:image/svg+xml;utf8,'); } p { color: red; - background: url('./b.jpg'); + background: url('./should-be-in-css-folder.jpg'); } body > p { color: yellow; diff --git a/packages/rrweb-snapshot/test/html/dialog.html b/packages/rrweb-snapshot/test/html/dialog.html new file mode 100644 index 0000000000..2380b8fade --- /dev/null +++ b/packages/rrweb-snapshot/test/html/dialog.html @@ -0,0 +1,5 @@ + + + I'm a dialog + + diff --git a/packages/rrweb-snapshot/test/html/monkey-patched-elements.html b/packages/rrweb-snapshot/test/html/monkey-patched-elements.html new file mode 100644 index 0000000000..a48b8fd328 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/monkey-patched-elements.html @@ -0,0 +1,45 @@ + + + + + + Document + + + + + + diff --git a/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html b/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html index 6b45f65bc5..d98ff7b9fa 100644 --- a/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html +++ b/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html @@ -7,6 +7,10 @@ with style sheet with import + diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index 8f4476ecb9..9fa04baf65 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -1,10 +1,20 @@ import * as fs from 'fs'; -import * as path from 'path'; import * as http from 'http'; -import * as url from 'url'; +import * as path from 'path'; import * as puppeteer from 'puppeteer'; -import { vi, assert, describe, it, beforeAll, afterAll, expect } from 'vitest'; -import { waitForRAF, getServerURL } from './utils'; +import * as url from 'url'; +import { + afterAll, + assert, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; + +import { getServerURL, waitForRAF } from './utils'; const htmlFolder = path.join(__dirname, 'html'); const htmls = fs.readdirSync(htmlFolder).map((filePath) => { @@ -60,6 +70,15 @@ function sanitizeSnapshot(snapshot: string): string { return snapshot.replace(/localhost:[0-9]+/g, 'localhost:3030'); } +async function snapshot(page: puppeteer.Page, code: string): Promise { + await waitForRAF(page); + const result = (await page.evaluate(`${code} + const snapshot = rrwebSnapshot.snapshot(document); + JSON.stringify(snapshot, null, 2); + `)) as string; + return result; +} + function assertSnapshot(snapshot: string): void { expect(sanitizeSnapshot(snapshot)).toMatchSnapshot(); } @@ -68,6 +87,7 @@ interface ISuite { server: http.Server; serverURL: string; browser: puppeteer.Browser; + page: puppeteer.Page; code: string; } @@ -100,6 +120,9 @@ describe('integration tests', function (this: ISuite) { if (html.filePath.substring(html.filePath.length - 1) === '~') { continue; } + // monkey patching breaks rebuild code + if (html.filePath.includes('monkey-patched-elements.html')) continue; + const title = '[html file]: ' + html.filePath; it(title, async () => { const page: puppeteer.Page = await browser.newPage(); @@ -153,7 +176,7 @@ describe('integration tests', function (this: ISuite) { 'blob:http://localhost:xxxx/...', ); - assertSnapshot(rebuildHtml); + await assertSnapshot(rebuildHtml); }); } @@ -235,7 +258,6 @@ iframe.contentDocument.querySelector('center').clientHeight it('correctly saves cross-origin images offline', async () => { const page: puppeteer.Page = await browser.newPage(); - await page.goto('about:blank', { waitUntil: 'load', }); @@ -348,7 +370,7 @@ iframe.contentDocument.querySelector('center').clientHeight it('should save background-clip: text; as the more compatible -webkit-background-clip: test;', async () => { const page: puppeteer.Page = await browser.newPage(); - await page.goto(`http://localhost:3030/html/background-clip-text.html`, { + await page.goto(`${serverURL}/html/background-clip-text.html`, { waitUntil: 'load', }); await waitForRAF(page); // wait for page to render @@ -366,13 +388,10 @@ iframe.contentDocument.querySelector('center').clientHeight it('images with inline onload should work', async () => { const page: puppeteer.Page = await browser.newPage(); - await page.goto( - 'http://localhost:3030/html/picture-with-inline-onload.html', - { - waitUntil: 'load', - }, - ); - await page.waitForSelector('img', { timeout: 1000 }); + await page.goto(`${serverURL}/html/picture-with-inline-onload.html`, { + waitUntil: 'load', + }); + await page.waitForSelector('img', { timeout: 2000 }); await page.evaluate(`${code}`); await page.evaluate(` var snapshot = rrwebSnapshot.snapshot(document, { @@ -386,6 +405,22 @@ iframe.contentDocument.querySelector('center').clientHeight )) as string; assert(fnName === 'onload'); }); + + it('should be able to record elements even when .childNodes has been monkey patched', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`${serverURL}/html/monkey-patched-elements.html`, { + waitUntil: 'load', + }); + await waitForRAF(page); // wait for page to render + const snapshotResult = JSON.stringify( + await page.evaluate(`${code}; + rrwebSnapshot.snapshot(document); + `), + null, + 2, + ); + expect(snapshotResult).toMatchSnapshot(); + }); }); describe('iframe integration tests', function (this: ISuite) { @@ -427,6 +462,53 @@ describe('iframe integration tests', function (this: ISuite) { null, 2, ); + await assertSnapshot(snapshotResult); + }); +}); + +describe('dialog integration tests', function (this: ISuite) { + vi.setConfig({ testTimeout: 30_000 }); + let server: ISuite['server']; + let serverURL: ISuite['serverURL']; + let browser: ISuite['browser']; + let code: ISuite['code']; + let page: ISuite['page']; + + beforeAll(async () => { + server = await startServer(); + serverURL = getServerURL(server); + browser = await puppeteer.launch({ + // headless: false, + }); + + code = fs.readFileSync( + path.resolve(__dirname, '../dist/rrweb-snapshot.umd.cjs'), + 'utf-8', + ); + }); + + beforeEach(async () => { + page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html/dialog.html`, { + waitUntil: 'load', + }); + }); + + afterAll(async () => { + await browser.close(); + await server.close(); + }); + + it('should capture open attribute for non modal dialogs', async () => { + page.evaluate('document.querySelector("dialog").show()'); + const snapshotResult = await snapshot(page, code); + assertSnapshot(snapshotResult); + }); + + it('should capture open attribute for modal dialogs', async () => { + await page.evaluate('document.querySelector("dialog").showModal()'); + const snapshotResult = await snapshot(page, code); assertSnapshot(snapshotResult); }); }); @@ -470,6 +552,6 @@ describe('shadow DOM integration tests', function (this: ISuite) { null, 2, ); - assertSnapshot(snapshotResult); + await assertSnapshot(snapshotResult); }); }); diff --git a/packages/rrweb-snapshot/test/rebuild.test.ts b/packages/rrweb-snapshot/test/rebuild.test.ts index c71e1e8510..a0994a2f88 100644 --- a/packages/rrweb-snapshot/test/rebuild.test.ts +++ b/packages/rrweb-snapshot/test/rebuild.test.ts @@ -3,14 +3,34 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import { describe, it, beforeEach, expect } from 'vitest'; +import { beforeEach, describe, expect as _expect, it } from 'vitest'; import { adaptCssForReplay, buildNodeWithSN, createCache, } from '../src/rebuild'; import { NodeType } from '../src/types'; -import { createMirror, Mirror } from '../src/utils'; +import { createMirror, Mirror, normalizeCssString } from '../src/utils'; + +const expect = _expect as unknown as { + (actual: T): { + toMatchCss(expected: string): void; + } & ReturnType; +} & typeof _expect; + +expect.extend({ + toMatchCss: function (received: string, expected: string) { + const pass = normalizeCssString(received) === normalizeCssString(expected); + const message: () => string = () => + pass + ? '' + : `Received (${received}) is not the same as expected (${expected})`; + return { + message, + pass, + }; + }, +}); function getDuration(hrtime: [number, number]) { const [seconds, nanoseconds] = hrtime; @@ -52,6 +72,32 @@ describe('rebuild', function () { }); }); + describe('rr_width/rr_height', function () { + it('rebuild blocked element with correct dimensions', function () { + const node = buildNodeWithSN( + { + id: 1, + tagName: 'svg', + type: NodeType.Element, + isSVG: true, + attributes: { + rr_width: '50px', + rr_height: '50px', + }, + childNodes: [], + }, + { + doc: document, + mirror, + hackCss: false, + cache, + }, + ) as HTMLDivElement; + expect(node.style.width).toBe('50px'); + expect(node.style.height).toBe('50px'); + }); + }); + describe('shadowDom', function () { it('rebuild shadowRoot without siblings', function () { const node = buildNodeWithSN( @@ -86,19 +132,19 @@ describe('rebuild', function () { describe('add hover class to hover selector related rules', function () { it('will do nothing to css text without :hover', () => { const cssText = 'body { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual(cssText); + expect(adaptCssForReplay(cssText, cache)).toMatchCss(cssText); }); it('can add hover class to css text', () => { const cssText = '.a:hover { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( '.a:hover, .a.\\:hover { color: white }', ); }); it('can correctly add hover when in middle of selector', () => { const cssText = 'ul li a:hover img { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( 'ul li a:hover img, ul li a.\\:hover img { color: white }', ); }); @@ -111,14 +157,15 @@ img, ul li.specified c:hover img { color: white }`; - expect(adaptCssForReplay(cssText, cache)).toEqual( - `ul li.specified a:hover img, ul li.specified a.\\:hover img, + expect(adaptCssForReplay(cssText, cache)).toMatchCss( + `ul li.specified a:hover img, ul li.multiline b:hover -img, ul li.multiline -b.\\:hover img, -ul li.specified c:hover img, ul li.specified c.\\:hover img { +ul li.specified c:hover img, +ul li.specified a.\\:hover img, +ul li.multiline b.\\:hover img, +ul li.specified c.\\:hover img { color: white }`, ); @@ -126,48 +173,48 @@ ul li.specified c:hover img, ul li.specified c.\\:hover img { it('can add hover class within media query', () => { const cssText = '@media screen { .m:hover { color: white } }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( '@media screen { .m:hover, .m.\\:hover { color: white } }', ); }); it('can add hover class when there is multi selector', () => { const cssText = '.a, .b:hover, .c { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( - '.a, .b:hover, .b.\\:hover, .c { color: white }', + expect(adaptCssForReplay(cssText, cache)).toMatchCss( + '.a, .b:hover, .c, .b.\\:hover { color: white }', ); }); it('can add hover class when there is a multi selector with the same prefix', () => { const cssText = '.a:hover, .a:hover::after { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( - '.a:hover, .a.\\:hover, .a:hover::after, .a.\\:hover::after { color: white }', + expect(adaptCssForReplay(cssText, cache)).toMatchCss( + '.a:hover, .a:hover::after, .a.\\:hover, .a.\\:hover::after { color: white }', ); }); it('can add hover class when :hover is not the end of selector', () => { const cssText = 'div:hover::after { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( 'div:hover::after, div.\\:hover::after { color: white }', ); }); it('can add hover class when the selector has multi :hover', () => { const cssText = 'a:hover b:hover { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( 'a:hover b:hover, a.\\:hover b.\\:hover { color: white }', ); }); it('will ignore :hover in css value', () => { const cssText = '.a::after { content: ":hover" }'; - expect(adaptCssForReplay(cssText, cache)).toEqual(cssText); + expect(adaptCssForReplay(cssText, cache)).toMatchCss(cssText); }); it('can adapt media rules to replay context', () => { const cssText = '@media only screen and (min-device-width : 1200px) { .a { width: 10px; }}'; - expect(adaptCssForReplay(cssText, cache)).toEqual( + expect(adaptCssForReplay(cssText, cache)).toMatchCss( '@media only screen and (min-width : 1200px) { .a { width: 10px; }}', ); }); @@ -210,7 +257,7 @@ ul li.specified c:hover img, ul li.specified c.\\:hover img { // previously that part was being incorrectly consumed by the selector regex const should_not_modify = ".tailwind :is(.before\\:content-\\[\\'\\'\\])::before { --tw-content: \":hover\"; content: var(--tw-content); }.tailwind :is(.\\[\\&\\>li\\]\\:before\\:content-\\[\\'-\\'\\] > li)::before { color: pink; }"; - expect(adaptCssForReplay(should_not_modify, cache)).toEqual( + expect(adaptCssForReplay(should_not_modify, cache)).toMatchCss( should_not_modify, ); }); @@ -219,7 +266,7 @@ ul li.specified c:hover img, ul li.specified c.\\:hover img { // the ':hover' in the below is a decoy which is not part of the selector, const should_not_modify = '@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,700;1,400&display=:hover");'; - expect(adaptCssForReplay(should_not_modify, cache)).toEqual( + expect(adaptCssForReplay(should_not_modify, cache)).toMatchCss( should_not_modify, ); }); diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index de1d79eb6d..5778eb0aff 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -2,64 +2,79 @@ * @vitest-environment jsdom */ import { JSDOM } from 'jsdom'; -import { describe, it, expect } from 'vitest'; -import { - absoluteToStylesheet, - serializeNodeWithId, +import { describe, expect, it } from 'vitest'; + +import snapshot, { _isBlockedElement, + serializeNodeWithId, } from '../src/snapshot'; -import snapshot from '../src/snapshot'; -import { serializedNodeWithId, elementNode } from '../src/types'; -import { Mirror } from '../src/utils'; +import { elementNode, serializedNodeWithId } from '../src/types'; +import { Mirror, absolutifyURLs } from '../src/utils'; + +const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + mirror: new Mirror(), + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + }); +}; describe('absolute url to stylesheet', () => { const href = 'http://localhost/css/style.css'; it('can handle relative path', () => { - expect(absoluteToStylesheet('url(a.jpg)', href)).toEqual( + expect(absolutifyURLs('url(a.jpg)', href)).toEqual( `url(http://localhost/css/a.jpg)`, ); }); it('can handle same level path', () => { - expect(absoluteToStylesheet('url("./a.jpg")', href)).toEqual( + expect(absolutifyURLs('url("./a.jpg")', href)).toEqual( `url("http://localhost/css/a.jpg")`, ); }); it('can handle parent level path', () => { - expect(absoluteToStylesheet('url("../a.jpg")', href)).toEqual( + expect(absolutifyURLs('url("../a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle absolute path', () => { - expect(absoluteToStylesheet('url("/a.jpg")', href)).toEqual( + expect(absolutifyURLs('url("/a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle external path', () => { - expect(absoluteToStylesheet('url("http://localhost/a.jpg")', href)).toEqual( + expect(absolutifyURLs('url("http://localhost/a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle single quote path', () => { - expect(absoluteToStylesheet(`url('./a.jpg')`, href)).toEqual( + expect(absolutifyURLs(`url('./a.jpg')`, href)).toEqual( `url('http://localhost/css/a.jpg')`, ); }); it('can handle no quote path', () => { - expect(absoluteToStylesheet('url(./a.jpg)', href)).toEqual( + expect(absolutifyURLs('url(./a.jpg)', href)).toEqual( `url(http://localhost/css/a.jpg)`, ); }); it('can handle multiple no quote paths', () => { expect( - absoluteToStylesheet( + absolutifyURLs( 'background-image: url(images/b.jpg);background: #aabbcc url(images/a.jpg) 50% 50% repeat;', href, ), @@ -70,11 +85,11 @@ describe('absolute url to stylesheet', () => { }); it('can handle data url image', () => { + expect(absolutifyURLs('url(data:image/gif;base64,ABC)', href)).toEqual( + 'url(data:image/gif;base64,ABC)', + ); expect( - absoluteToStylesheet('url(data:image/gif;base64,ABC)', href), - ).toEqual('url(data:image/gif;base64,ABC)'); - expect( - absoluteToStylesheet( + absolutifyURLs( 'url(data:application/font-woff;base64,d09GMgABAAAAAAm)', href, ), @@ -83,7 +98,7 @@ describe('absolute url to stylesheet', () => { it('preserves quotes around inline svgs with spaces', () => { expect( - absoluteToStylesheet( + absolutifyURLs( "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")", href, ), @@ -91,7 +106,7 @@ describe('absolute url to stylesheet', () => { "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")", ); expect( - absoluteToStylesheet( + absolutifyURLs( 'url(\'data:image/svg+xml;utf8,\')', href, ), @@ -99,7 +114,7 @@ describe('absolute url to stylesheet', () => { 'url(\'data:image/svg+xml;utf8,\')', ); expect( - absoluteToStylesheet( + absolutifyURLs( 'url("data:image/svg+xml;utf8,")', href, ), @@ -108,7 +123,7 @@ describe('absolute url to stylesheet', () => { ); }); it('can handle empty path', () => { - expect(absoluteToStylesheet(`url('')`, href)).toEqual(`url('')`); + expect(absolutifyURLs(`url('')`, href)).toEqual(`url('')`); }); }); @@ -139,22 +154,6 @@ describe('isBlockedElement()', () => { }); describe('style elements', () => { - const serializeNode = (node: Node): serializedNodeWithId | null => { - return serializeNodeWithId(node, { - doc: document, - mirror: new Mirror(), - blockClass: 'blockblock', - blockSelector: null, - maskTextClass: 'maskmask', - maskTextSelector: null, - skipChild: false, - inlineStylesheet: true, - maskTextFn: undefined, - maskInputFn: undefined, - slimDOMOptions: {}, - }); - }; - const render = (html: string): HTMLStyleElement => { document.write(html); return document.querySelector('style')!; @@ -163,44 +162,32 @@ describe('style elements', () => { it('should serialize all rules of stylesheet when the sheet has a single child node', () => { const styleEl = render(``); styleEl.sheet?.insertRule('section { color: blue; }'); - expect(serializeNode(styleEl.childNodes[0])).toMatchObject({ - isStyle: true, + expect(serializeNode(styleEl)).toMatchObject({ rootId: undefined, - textContent: 'section {color: blue;}body {color: red;}', - type: 3, + attributes: { + _cssText: 'section {color: blue;}body {color: red;}', + }, + type: 2, }); }); - it('should serialize individual text nodes on stylesheets with multiple child nodes', () => { + it('should serialize all rules on stylesheets with mix of insertion type', () => { const styleEl = render(``); + styleEl.sheet?.insertRule('section.lost { color: unseeable; }'); // browser throws this away after append styleEl.append(document.createTextNode('section { color: blue; }')); - expect(serializeNode(styleEl.childNodes[1])).toMatchObject({ - isStyle: true, + styleEl.sheet?.insertRule('section.working { color: pink; }'); + expect(serializeNode(styleEl)).toMatchObject({ rootId: undefined, - textContent: 'section { color: blue; }', - type: 3, + attributes: { + _cssText: + 'section.working {color: pink;}body {color: red;}/* rr_split */section {color: blue;}', + }, + type: 2, }); }); }); describe('scrollTop/scrollLeft', () => { - const serializeNode = (node: Node): serializedNodeWithId | null => { - return serializeNodeWithId(node, { - doc: document, - mirror: new Mirror(), - blockClass: 'blockblock', - blockSelector: null, - maskTextClass: 'maskmask', - maskTextSelector: null, - skipChild: false, - inlineStylesheet: true, - maskTextFn: undefined, - maskInputFn: undefined, - slimDOMOptions: {}, - newlyAddedElement: false, - }); - }; - const render = (html: string): HTMLDivElement => { document.write(html); return document.querySelector('div')!; @@ -222,23 +209,6 @@ describe('scrollTop/scrollLeft', () => { }); describe('form', () => { - const serializeNode = (node: Node): serializedNodeWithId | null => { - return serializeNodeWithId(node, { - doc: document, - mirror: new Mirror(), - blockClass: 'blockblock', - blockSelector: null, - maskTextClass: 'maskmask', - maskTextSelector: null, - skipChild: false, - inlineStylesheet: true, - maskTextFn: undefined, - maskInputFn: undefined, - slimDOMOptions: {}, - newlyAddedElement: false, - }); - }; - const render = (html: string): HTMLTextAreaElement => { document.write(html); return document.querySelector('textarea')!; diff --git a/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts b/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts new file mode 100644 index 0000000000..1e42bab1a6 --- /dev/null +++ b/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts @@ -0,0 +1,37 @@ +/** + * @vitest-environment jsdom + */ +import { bench } from 'vitest'; +import * as fs from 'fs'; +import * as path from 'path'; +import { stringifyStylesheet } from '../src/utils'; +import * as CSSOM from 'cssom'; + +describe('stringifyStylesheet', () => { + let benchmarkStylesheet: CSSStyleSheet; + + const cssText = fs.readFileSync( + path.resolve(__dirname, './css/benchmark.css'), + 'utf8', + ); + benchmarkStylesheet = CSSOM.parse(cssText); + benchmarkStylesheet.href = 'https://example.com/style.css'; + + it.skip('stringify', () => { + // written just to ensure it's working + const cssText = '.x { background: url(./relative.jpg) }'; + const styleSheet = CSSOM.parse(cssText); + styleSheet.href = 'https://example.com/style.css'; + expect(stringifyStylesheet(styleSheet)).toEqual( + 'x {background: url(https://example.com/relative.jpg);}', + ); + }); + + bench( + 'stringify', + () => { + stringifyStylesheet(benchmarkStylesheet); + }, + { time: 1000 }, + ); +}); diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts index 2b09602ae4..885f4ec385 100644 --- a/packages/rrweb-snapshot/test/utils.test.ts +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -3,7 +3,12 @@ */ import { describe, it, test, expect } from 'vitest'; import { NodeType, serializedNode } from '../src/types'; -import { extractFileExtension, isNodeMetaEqual } from '../src/utils'; +import { + escapeImportStatement, + extractFileExtension, + fixSafariColons, + isNodeMetaEqual, +} from '../src/utils'; import type { serializedNodeWithId } from '@saola.ai/rrweb-snapshot'; describe('utils', () => { @@ -199,4 +204,80 @@ describe('utils', () => { expect(extension).toBe('js'); }); }); + + describe('escapeImportStatement', () => { + it('parses imports with quotes correctly', () => { + const out1 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"");`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: null, + supportsText: null, + } as unknown as CSSImportRule); + expect(out1).toEqual(`@import url("/foo.css;900;800\\"");`); + + const out2 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") supports(display: flex);`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: null, + supportsText: 'display: flex', + } as unknown as CSSImportRule); + expect(out2).toEqual( + `@import url("/foo.css;900;800\\"") supports(display: flex);`, + ); + + const out3 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"");`, + href: '/foo.css;900;800"', + media: { + length: 1, + mediaText: 'print, screen', + }, + layerName: null, + supportsText: null, + } as unknown as CSSImportRule); + expect(out3).toEqual(`@import url("/foo.css;900;800\\"") print, screen;`); + + const out4 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") layer(layer-1);`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: 'layer-1', + supportsText: null, + } as unknown as CSSImportRule); + expect(out4).toEqual( + `@import url("/foo.css;900;800\\"") layer(layer-1);`, + ); + + const out5 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") layer;`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: '', + supportsText: null, + } as unknown as CSSImportRule); + expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`); + }); + }); + describe('fixSafariColons', () => { + it('parses : in attribute selectors correctly', () => { + const out1 = fixSafariColons('[data-foo] { color: red; }'); + expect(out1).toEqual('[data-foo] { color: red; }'); + + const out2 = fixSafariColons('[data-foo:other] { color: red; }'); + expect(out2).toEqual('[data-foo\\:other] { color: red; }'); + + const out3 = fixSafariColons('[data-aa\\:other] { color: red; }'); + expect(out3).toEqual('[data-aa\\:other] { color: red; }'); + }); + }); }); diff --git a/packages/rrweb-snapshot/tsconfig.json b/packages/rrweb-snapshot/tsconfig.json index 82d5cc086b..cd2eb36538 100644 --- a/packages/rrweb-snapshot/tsconfig.json +++ b/packages/rrweb-snapshot/tsconfig.json @@ -1,7 +1,13 @@ { "extends": "../../tsconfig.base.json", - "include": ["src"], - "exclude": ["vite.config.ts", "vitest.config.ts", "test"], + "include": [ + "src" + ], + "exclude": [ + "vite.config.ts", + "vitest.config.ts", + "test" + ], "compilerOptions": { "rootDir": "src", "tsBuildInfoFile": "./tsconfig.tsbuildinfo" diff --git a/packages/rrweb-snapshot/vitest.config.ts b/packages/rrweb-snapshot/vitest.config.ts index 39888437cf..1b5a8b7e3e 100644 --- a/packages/rrweb-snapshot/vitest.config.ts +++ b/packages/rrweb-snapshot/vitest.config.ts @@ -6,7 +6,7 @@ export default mergeConfig( configShared, defineProject({ test: { - // ... custom test config here + globals: true, }, }), ); diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index 8686ccbba0..69641c0e3d 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,52 @@ # rrweb +## 2.0.13 + +### Patch Changes + +- Merge from rrweb remote upstream + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.13 + - @saola.ai/rrdom@2.0.13 + - @saola.ai/rrweb-types@2.0.13 + - @saola.ai/rrweb-utils@2.0.13 + +## 2.0.0-alpha.17 + +### Minor Changes + +- [#1503](https://github.com/rrweb-io/rrweb/pull/1503) [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158) Thanks [@Juice10](https://github.com/Juice10)! - Support top-layer components. Fixes #1381. + +### Patch Changes + +- [#1417](https://github.com/rrweb-io/rrweb/pull/1417) [`40bbc25`](https://github.com/rrweb-io/rrweb/commit/40bbc25fc287badc317a53f2d3f21b1c9f2b211b) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - fix: duplicate textContent for style elements cause incremental style mutations to be invalid + +- [#1527](https://github.com/rrweb-io/rrweb/pull/1527) [`68076b7`](https://github.com/rrweb-io/rrweb/commit/68076b724ff19d198d4f351a05063b85e1705a8c) Thanks [@arredgroup](https://github.com/arredgroup)! - Export takeFullSnapshot function for a recording process + +- [#1515](https://github.com/rrweb-io/rrweb/pull/1515) [`8059d96`](https://github.com/rrweb-io/rrweb/commit/8059d9695146626b102b2059a3a9b932d5f598f6) Thanks [@okejminja](https://github.com/okejminja)! - Added support for deprecated addRule & removeRule methods + +- [#1509](https://github.com/rrweb-io/rrweb/pull/1509) [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414) Thanks [@Juice10](https://github.com/Juice10)! - Reverse monkey patch built in methods to support LWC (and other frameworks like angular which monkey patch built in methods). + +- Updated dependencies [[`40bbc25`](https://github.com/rrweb-io/rrweb/commit/40bbc25fc287badc317a53f2d3f21b1c9f2b211b), [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158), [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158), [`d350da8`](https://github.com/rrweb-io/rrweb/commit/d350da8552d8616dd118ee550bdfbce082986562), [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414)]: + - rrweb-snapshot@2.0.0-alpha.17 + - rrdom@2.0.0-alpha.17 + - @rrweb/types@2.0.0-alpha.17 + - @rrweb/utils@2.0.0-alpha.17 + +## 2.0.0-alpha.16 + +### Patch Changes + +- [#1386](https://github.com/rrweb-io/rrweb/pull/1386) [`a2c8a1a`](https://github.com/rrweb-io/rrweb/commit/a2c8a1a37bfcf8389b280af792262c8263a979a3) Thanks [@ababik](https://github.com/ababik)! - Fix that the optional `maskInputFn` was being accidentally ignored during the creation of the full snapshot + +- [#1512](https://github.com/rrweb-io/rrweb/pull/1512) [`d08624c`](https://github.com/rrweb-io/rrweb/commit/d08624cb28add386c3618a0e6607424c3f1884d8) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - optimisation: skip mask check on leaf elements + +- Updated dependencies [[`a2c8a1a`](https://github.com/rrweb-io/rrweb/commit/a2c8a1a37bfcf8389b280af792262c8263a979a3), [`d08624c`](https://github.com/rrweb-io/rrweb/commit/d08624cb28add386c3618a0e6607424c3f1884d8)]: + - rrweb-snapshot@2.0.0-alpha.16 + - rrdom@2.0.0-alpha.16 + - @rrweb/types@2.0.0-alpha.16 + ## 2.0.12 ### Patch Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 24e790a36d..3345ec6f26 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,24 +1,25 @@ { "name": "@saola.ai/rrweb", - "version": "2.0.12", + "version": "2.0.13", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", "prepack": "npm run build", - "retest": "vitest run --exclude test/benchmark", + "retest": "cross-env PUPPETEER_HEADLESS=true yarn retest:headful", + "retest:headful": "vitest run --exclude test/benchmark", "build-and-test": "yarn build && yarn retest", - "test:headless": "PUPPETEER_HEADLESS=true yarn build-and-test", - "test:headful": "PUPPETEER_HEADLESS=false yarn build-and-test", + "test:headless": "cross-env PUPPETEER_HEADLESS=true yarn build-and-test", + "test:headful": "cross-env PUPPETEER_HEADLESS=false yarn build-and-test", "test": "yarn test:headless", - "test:watch": "yarn build && PUPPETEER_HEADLESS=true yarn vitest --exclude test/benchmark", + "test:watch": "yarn build && cross-env PUPPETEER_HEADLESS=true yarn vitest --exclude test/benchmark", "test:update": "yarn test:headless --update", - "retest:update": "PUPPETEER_HEADLESS=true yarn retest --update", + "retest:update": "cross-env PUPPETEER_HEADLESS=true yarn retest --update", "repl": "yarn build && node scripts/repl.js", "live-stream": "yarn build && node scripts/stream.js", "dev": "vite build --watch", - "build": "tsc -noEmit && vite build", + "build": "yarn turbo run prepublish", "check-types": "tsc -noEmit", - "prepublish": "npm run build", + "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src", "benchmark": "vitest run --maxConcurrency 1 --no-file-parallelism test/benchmark" }, @@ -68,7 +69,6 @@ "@types/node": "^18.15.11", "@types/offscreencanvas": "^2019.6.4", "construct-style-sheets-polyfill": "^3.1.0", - "cross-env": "^5.2.0", "fast-mhtml": "^1.1.9", "identity-obj-proxy": "^3.0.0", "ignore-styles": "^5.0.1", @@ -78,17 +78,18 @@ "simple-peer-light": "^9.10.0", "ts-node": "^10.9.1", "tslib": "^2.3.1", - "typescript": "^4.7.3", - "vite": "^5.2.8", - "vite-plugin-dts": "^3.8.1" + "typescript": "^5.4.5", + "vite": "^5.3.1", + "vite-plugin-dts": "^3.9.1" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.12", + "@saola.ai/rrweb-types": "^2.0.13", + "@saola.ai/rrweb-utils": "^2.0.13", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", - "@saola.ai/rrdom": "^2.0.12", - "@saola.ai/rrweb-snapshot": "^2.0.12" + "@saola.ai/rrdom": "^2.0.13", + "@saola.ai/rrweb-snapshot": "^2.0.13" } } diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index e013aab4e7..cbb8c566d9 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -17,19 +17,24 @@ export { type eventWithTime, } from '@saola.ai/rrweb-types'; +// exports style.css from replay +import './replay/styles/style.css'; + export type { recordOptions, ReplayPlugin } from './types'; const { addCustomEvent } = record; const { freezePage } = record; +const { takeFullSnapshot } = record; export { record, addCustomEvent, freezePage, + takeFullSnapshot, Replayer, - playerConfig, - PlayerMachineState, - SpeedMachineState, + type playerConfig, + type PlayerMachineState, + type SpeedMachineState, canvasMutation, _mirror as mirror, utils, diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 7320a3c61e..a5bdd977b8 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -1,7 +1,7 @@ import { snapshot, - MaskInputOptions, - SlimDOMOptions, + type MaskInputOptions, + type SlimDOMOptions, createMirror, } from '@saola.ai/rrweb-snapshot'; import { initObservers, mutationBuffers } from './observer'; @@ -19,14 +19,14 @@ import { import type { recordOptions } from '../types'; import { EventType, - eventWithoutTime, - eventWithTime, + type eventWithoutTime, + type eventWithTime, IncrementalSource, - listenerHandler, - mutationCallbackParam, - scrollCallback, - canvasMutationParam, - adoptedStyleSheetParam, + type listenerHandler, + type mutationCallbackParam, + type scrollCallback, + type canvasMutationParam, + type adoptedStyleSheetParam, } from '@saola.ai/rrweb-types'; import type { CrossOriginIframeMessageEventContent } from '../types'; import { IframeManager } from './iframe-manager'; @@ -39,6 +39,7 @@ import { registerErrorHandler, unregisterErrorHandler, } from './error-handler'; +import dom from '@rrweb/utils'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; @@ -383,6 +384,7 @@ function record( inlineStylesheet, maskAllInputs: maskInputOptions, maskTextFn, + maskInputFn, slimDOM: slimDOMOptions, dataURLOptions, recordCanvas, @@ -395,7 +397,8 @@ function record( stylesheetManager.trackLinkElement(n as HTMLLinkElement); } if (hasShadowRoot(n)) { - shadowDomManager.addShadowRoot(n.shadowRoot, document); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + shadowDomManager.addShadowRoot(dom.shadowRoot(n as Node)!, document); } }, onIframeLoad: (iframe, childSn) => { diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 23ad493654..53537d03d2 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -32,6 +32,7 @@ import { getShadowHost, closestElementOfNode, } from '../utils'; +import dom from '@rrweb/utils'; type DoubleLinkedListNode = { previous: DoubleLinkedListNode | null; @@ -168,6 +169,7 @@ export default class MutationBuffer { private addedSet = new Set(); private movedSet = new Set(); private droppedSet = new Set(); + private removesSubTreeCache = new Set(); private mutationCb: observerParam['mutationCb']; private blockClass: observerParam['blockClass']; @@ -285,16 +287,27 @@ export default class MutationBuffer { return nextId; }; const pushAdd = (n: Node) => { - if ( - !n.parentNode || - !inDom(n) || - (n.parentNode as Element).tagName === 'TEXTAREA' - ) { + const parent = dom.parentNode(n); + if (!parent || !inDom(n)) { return; } - const parentId = isShadowRoot(n.parentNode) + let cssCaptured = false; + if (n.nodeType === Node.TEXT_NODE) { + const parentTag = (parent as Element).tagName; + if (parentTag === 'TEXTAREA') { + // genTextAreaValueMutation already called via parent + return; + } else if (parentTag === 'STYLE' && this.addedSet.has(parent)) { + // css content will be recorded via parent's _cssText attribute when + // mutation adds entire + + + + + + + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 34d81da850..0ae70af289 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -81,7 +81,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can record form interactions', async () => { @@ -98,10 +98,10 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); - it('can record textarea mutations correctly', async () => { + it('can record and replay textarea mutations correctly', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'empty.html')); @@ -112,20 +112,29 @@ describe('record integration tests', function (this: ISuite) { const ta = document.createElement('textarea'); ta.innerText = 'pre value'; document.body.append(ta); + + const ta2 = document.createElement('textarea'); + ta2.id = 'ta2'; + document.body.append(ta2); }); - await page.waitForTimeout(5); + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; t.innerText = 'ok'; // this mutation should be recorded + + const ta2t = document.createTextNode('added'); + document.getElementById('ta2').append(ta2t); }); - await page.waitForTimeout(5); + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; (t.childNodes[0] as Text).appendData('3'); // this mutation is also valid + + document.getElementById('ta2').remove(); // done with this }); - await page.waitForTimeout(5); + await waitForRAF(page); await page.type('textarea', '1'); // types (inserts) at index 0, in front of existing text - await page.waitForTimeout(5); + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; // user has typed so childNode content should now be ignored @@ -136,13 +145,13 @@ describe('record integration tests', function (this: ISuite) { // there is nothing explicit in rrweb which enforces this, but this test may protect against // a future change where a mutation on a textarea incorrectly updates the .value }); - await page.waitForTimeout(5); + await waitForRAF(page); await page.type('textarea', '2'); // cursor is at index 1 const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); // check after each mutation and text input const replayTextareaValues = await page.evaluate(` @@ -153,12 +162,18 @@ describe('record integration tests', function (this: ISuite) { replayer.pause((e.timestamp - window.snapshots[0].timestamp)+1); let ts = replayer.iframe.contentDocument.querySelector('textarea'); vals.push((e.data.source === 0 ? 'Mutation' : 'User') + ':' + ts.value); + let ts2 = replayer.iframe.contentDocument.getElementById('ta2'); + if (ts2) { + vals.push('ta2:' + ts2.value); + } }); vals; `); expect(replayTextareaValues).toEqual([ 'Mutation:pre value', + 'ta2:', 'Mutation:ok', + 'ta2:added', 'Mutation:ok3', 'User:1ok3', 'Mutation:1ok3', // if this gets set to 'ignore', it's an error, as the 'user' has modified the textarea @@ -166,6 +181,131 @@ describe('record integration tests', function (this: ISuite) { ]); }); + it('can record and replay style mutations', async () => { + // This test shows that the `isStyle` attribute on textContent is not needed in a mutation + // TODO: we could get a lot more elaborate here with mixed textContent and insertRule mutations + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`${serverURL}/html`); + await page.setContent(getHtml.call(this, 'style.html')); + + await waitForRAF(page); // ensure mutations aren't included in fullsnapshot + + await page.evaluate(() => { + let styleEl = document.querySelector('style#dual-textContent'); + if (styleEl) { + styleEl.append( + document.createTextNode('body { background-color: darkgreen; }'), + ); + styleEl.append( + document.createTextNode( + '.absolutify { background-image: url("./rel"); }', + ), + ); + } + }); + await waitForRAF(page); + await page.evaluate(() => { + let styleEl = document.querySelector('style#dual-textContent'); + if (styleEl) { + styleEl.childNodes.forEach((cn) => { + if (cn.textContent) { + cn.textContent = cn.textContent.replace('darkgreen', 'purple'); + cn.textContent = cn.textContent.replace( + 'orange !important', + 'yellow', + ); + } + }); + } + }); + await waitForRAF(page); + await page.evaluate(() => { + let styleEl = document.querySelector('style#dual-textContent'); + if (styleEl) { + styleEl.childNodes.forEach((cn) => { + if (cn.textContent) { + cn.textContent = cn.textContent.replace( + 'black', + 'black !important', + ); + } + }); + } + let hoverMutationStyleEl = document.querySelector('style#hover-mutation'); + if (hoverMutationStyleEl) { + hoverMutationStyleEl.childNodes.forEach((cn) => { + if (cn.textContent) { + cn.textContent = 'a:hover { outline: cyan solid 1px; }'; + } + }); + } + let st = document.createElement('style'); + st.id = 'goldilocks'; + st.innerText = 'body { color: brown }'; + document.body.append(st); + }); + + await waitForRAF(page); + await page.evaluate(() => { + let styleEl = document.querySelector('style#goldilocks'); + if (styleEl) { + styleEl.childNodes.forEach((cn) => { + if (cn.textContent) { + cn.textContent = cn.textContent.replace('brown', 'gold'); + } + }); + } + }); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + + // following ensures that the ./rel url has been absolutized (in a mutation) + await assertSnapshot(snapshots); + + // check after each mutation and text input + const replayStyleValues = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(window.snapshots); + const vals = []; + window.snapshots.filter((e)=>e.data.attributes || e.data.source === 5).forEach((e)=>{ + replayer.pause((e.timestamp - window.snapshots[0].timestamp)+1); + let bodyStyle = getComputedStyle(replayer.iframe.contentDocument.querySelector('body')) + vals.push({ + 'background-color': bodyStyle['background-color'], + 'color': bodyStyle['color'], + }); + }); + vals.push(replayer.iframe.contentDocument.getElementById('single-textContent').innerText); + vals.push(replayer.iframe.contentDocument.getElementById('empty').innerText); + vals.push(replayer.iframe.contentDocument.getElementById('hover-mutation').innerText); + vals; +`); + + expect(replayStyleValues).toEqual([ + { + 'background-color': 'rgb(0, 100, 0)', // darkgreen + color: 'rgb(255, 165, 0)', // orange (from style.html) + }, + { + 'background-color': 'rgb(128, 0, 128)', // purple + color: 'rgb(255, 255, 0)', // yellow + }, + { + 'background-color': 'rgb(0, 0, 0)', // black !important + color: 'rgb(165, 42, 42)', // brown + }, + { + 'background-color': 'rgb(0, 0, 0)', + color: 'rgb(255, 215, 0)', // gold + }, + 'a:hover,\na.\\:hover { outline: red solid 1px; }', // has run adaptCssForReplay + 'a:hover,\na.\\:hover { outline: blue solid 1px; }', // has run adaptCssForReplay + 'a:hover,\na.\\:hover { outline: cyan solid 1px; }', // has run adaptCssForReplay after text mutation + ]); + }); + it('can record childList mutations', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); @@ -183,7 +323,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can record character data muatations', async () => { @@ -205,7 +345,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can record attribute mutation', async () => { @@ -225,7 +365,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('handles null attribute values', async () => { @@ -253,7 +393,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can record node mutations', async () => { @@ -272,7 +412,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can record style changes compactly and preserve css var() functions', async () => { @@ -322,7 +462,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can freeze mutations', async () => { @@ -358,7 +498,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should not record input events on ignored elements', async () => { @@ -394,7 +534,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can use maskInputOptions to configure which type of inputs should be masked', async () => { @@ -420,7 +560,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should mask password value attribute with maskInputOptions', async () => { @@ -444,7 +584,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should mask inputs via function call', async () => { @@ -474,7 +614,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record input userTriggered values if userTriggeredOnInput is enabled', async () => { @@ -494,7 +634,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should not record blocked elements and its child nodes', async () => { @@ -509,7 +649,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should not record blocked elements dynamically added', async () => { @@ -531,7 +671,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('mutations should work when blocked class is unblocked', async () => { @@ -552,7 +692,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record DOM node movement 1', async () => { @@ -572,7 +712,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record DOM node movement 2', async () => { @@ -589,7 +729,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record dynamic CSS changes', async () => { @@ -600,7 +740,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record canvas mutations', async () => { @@ -625,7 +765,7 @@ describe('record integration tests', function (this: ISuite) { }); } } - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should not record input values if dynamically added and maskAllInputs is true', async () => { @@ -662,7 +802,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can correctly serialize a shader and multiple webgl contexts', async () => { @@ -677,7 +817,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('will serialize node before record', async () => { @@ -698,7 +838,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('will defer missing next node mutation', async () => { @@ -735,7 +875,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record images with blob url', async () => { @@ -752,7 +892,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record images inside iframe with blob url', async () => { @@ -769,7 +909,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record images inside iframe with blob url after iframe was reloaded', async () => { @@ -792,7 +932,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record shadow DOM', async () => { @@ -842,7 +982,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record shadow DOM 2', async () => { @@ -867,7 +1007,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record shadow DOM 3', async () => { @@ -888,7 +1028,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record moved shadow DOM', async () => { @@ -917,7 +1057,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record moved shadow DOM 2', async () => { @@ -955,7 +1095,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record nested iframes and shadow doms', async () => { @@ -1000,7 +1140,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record mutations in iframes accross pages', async () => { @@ -1030,7 +1170,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); // https://github.com/webcomponents/polyfills/tree/master/packages/shadydom @@ -1064,7 +1204,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); // https://github.com/salesforce/lwc/tree/master/packages/%40lwc/synthetic-shadow @@ -1106,7 +1246,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should mask texts', async () => { @@ -1121,7 +1261,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should mask texts using maskTextFn', async () => { @@ -1137,7 +1277,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should unmask texts using maskTextFn', async () => { @@ -1157,7 +1297,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can mask character data mutations', async () => { @@ -1186,7 +1326,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('can mask character data mutations with regexp', async () => { @@ -1219,7 +1359,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record after DOMContentLoaded event', async () => { @@ -1234,6 +1374,175 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); + }); + + /** + * the regression part of the following is now handled by replayer.test.ts::'can deal with duplicate/conflicting values on style elements' + * so this test could be dropped if we add more robust mixing of `insertRule` into 'can record and replay style mutations' + */ + it('should record style mutations and replay them correctly', async () => { + const page: puppeteer.Page = await browser.newPage(); + const OldColor = 'rgb(255, 0, 0)'; // red color + const NewColor = 'rgb(255, 255, 0)'; // yellow color + + await page.setContent( + ` + + + + + +
+
+ + + `, + ); + // Start rrweb recording + await page.evaluate( + (code, recordSnippet) => { + const script = document.createElement('script'); + script.textContent = `${code}window.Date.now = () => new Date(Date.UTC(2018, 10, 15, 8)).valueOf();${recordSnippet}`; + document.head.appendChild(script); + }, + code, + generateRecordSnippet({}), + ); + + await page.evaluate( + async (OldColor, NewColor) => { + // Create a new style element with the same content as the existing style element and apply it to the #two div element + const incrementalStyle = document.createElement( + 'style', + ) as HTMLStyleElement; + incrementalStyle.textContent = ` \n`; + document.head.appendChild(incrementalStyle); + incrementalStyle.sheet!.insertRule(`#two { color: ${OldColor}; }`, 0); + + await new Promise((resolve) => + requestAnimationFrame(() => { + requestAnimationFrame(resolve); + }), + ); + + // Change the color of the #one div element to yellow as an incremental style mutation + const styleElement = document.querySelector('style')!; + (styleElement.sheet!.cssRules[0] as any).style.setProperty( + 'color', + NewColor, + ); + // Change the color of the #two div element to yellow as an incremental style mutation + (incrementalStyle.sheet!.cssRules[0] as any).style.setProperty( + 'color', + NewColor, + ); + }, + OldColor, + NewColor, + ); + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + await assertSnapshot(snapshots); + + /** + * Replay the recorded events and check if the style mutation is applied correctly + */ + const changedColors = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(window.snapshots); + replayer.pause(1000); + + // Get the color of the element after applying the style mutation event + [ + window.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#one'), + ).color, + window.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#two'), + ).color, + ]; + `); + expect(changedColors).toEqual([NewColor, NewColor]); + await page.close(); + }); + + it('should record style mutations with multiple child nodes and replay them correctly', async () => { + // ensure that presence of multiple text nodes doesn't interfere with programmatic insertRule operations + + const page: puppeteer.Page = await browser.newPage(); + const Color = 'rgb(255, 0, 0)'; // red color + + await page.setContent( + ` + + + + + +
+
+ + + `, + ); + // Start rrweb recording + await page.evaluate( + (code, recordSnippet) => { + const script = document.createElement('script'); + script.textContent = `${code};${recordSnippet}`; + document.head.appendChild(script); + }, + code, + generateRecordSnippet({}), + ); + + await page.evaluate(async (Color) => { + // Create a new style element with the same content as the existing style element and apply it to the #two div element + const incrementalStyle = document.createElement( + 'style', + ) as HTMLStyleElement; + incrementalStyle.append(document.createTextNode('/* hello */')); + incrementalStyle.append(document.createTextNode('/* world */')); + document.head.appendChild(incrementalStyle); + incrementalStyle.sheet!.insertRule(`#two { color: ${Color}; }`, 0); + }, Color); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + await assertSnapshot(snapshots); + + /** + * Replay the recorded events and check if the style mutation is applied correctly + */ + const changedColors = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(window.snapshots); + replayer.pause(1000); + + // Get the color of the element after applying the style mutation event + [ + window.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#one'), + ).color, + window.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#two'), + ).color, + ]; + `); + expect(changedColors).toEqual([Color, Color]); + await page.close(); }); }); diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index d35dd6f6a2..72a8a7fbe8 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -206,7 +206,7 @@ describe('record', function (this: ISuite) { }, 10); }); await ctx.page.waitForTimeout(100); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('should record scroll position', async () => { @@ -223,7 +223,7 @@ describe('record', function (this: ISuite) { p.scrollLeft = 10; }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('should record selection event', async () => { @@ -279,7 +279,7 @@ describe('record', function (this: ISuite) { }); }); await ctx.page.waitForTimeout(50); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures stylesheet rules', async () => { @@ -297,6 +297,7 @@ describe('record', function (this: ISuite) { // begin: pre-serialization const ruleIdx0 = styleSheet.insertRule('body { background: #000; }'); const ruleIdx1 = styleSheet.insertRule('body { background: #111; }'); + styleSheet.deleteRule(ruleIdx1); // end: pre-serialization setTimeout(() => { @@ -328,8 +329,71 @@ describe('record', function (this: ISuite) { rule: 'body { color: #fff; }', }, ]); + expect((addRules[1].data as styleSheetRuleData).adds).toEqual([ + { + rule: 'body { color: #ccc; }', + }, + ]); + expect(removeRuleCount).toEqual(1); + await assertSnapshot(ctx.events); + }); + + it('captures stylesheet rules with deprecated addRule & removeRule properties', async () => { + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + + record({ + emit: (window as unknown as IWindow).emit, + }); + + const styleElement = document.createElement('style'); + document.head.appendChild(styleElement); + + const styleSheet = styleElement.sheet; + // begin: pre-serialization + const ruleIdx0 = styleSheet.addRule('body', 'background: #000;'); + const ruleIdx1 = styleSheet.addRule('body', 'background: #111;'); + + styleSheet.removeRule(ruleIdx1); + // end: pre-serialization + setTimeout(() => { + styleSheet.addRule('body', 'color: #fff;'); + }, 0); + setTimeout(() => { + styleSheet.removeRule(ruleIdx0); + }, 5); + setTimeout(() => { + styleSheet.addRule('body', 'color: #ccc;'); + }, 10); + }); + await ctx.page.waitForTimeout(50); + const styleSheetRuleEvents = ctx.events.filter( + (e) => + e.type === EventType.IncrementalSnapshot && + e.data.source === IncrementalSource.StyleSheetRule, + ); + const addRules = styleSheetRuleEvents.filter((e) => + Boolean((e.data as styleSheetRuleData).adds), + ); + const removeRuleCount = styleSheetRuleEvents.filter((e) => + Boolean((e.data as styleSheetRuleData).removes), + ).length; + // pre-serialization insert/delete should be ignored + expect(addRules.length).toEqual(2); + expect((addRules[0].data as styleSheetRuleData).adds).toEqual([ + { + index: 1, + rule: 'body { color: #fff; }', + }, + ]); + expect((addRules[1].data as styleSheetRuleData).adds).toEqual([ + { + index: 1, + rule: 'body { color: #ccc; }', + }, + ]); expect(removeRuleCount).toEqual(1); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); const captureNestedStylesheetRulesTest = async () => { @@ -375,7 +439,7 @@ describe('record', function (this: ISuite) { // sync insert/delete should be ignored expect(addRuleCount).toEqual(2); expect(removeRuleCount).toEqual(1); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }; it('captures nested stylesheet rules', captureNestedStylesheetRulesTest); @@ -426,7 +490,7 @@ describe('record', function (this: ISuite) { }, 0); }); await ctx.page.waitForTimeout(50); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures inserted style text nodes correctly', async () => { @@ -446,7 +510,7 @@ describe('record', function (this: ISuite) { styleEl.append(document.createTextNode('h1 { color: pink; }')); }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures stylesheets with `blob:` url', async () => { @@ -473,7 +537,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures mutations on adopted stylesheets', async () => { @@ -538,7 +602,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures adopted stylesheets in nested shadow doms and iframes', async () => { @@ -590,7 +654,7 @@ describe('record', function (this: ISuite) { }, 150); }); await ctx.page.waitForTimeout(200); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures adopted stylesheets of shadow doms in checkout full snapshot', async () => { @@ -619,7 +683,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures stylesheets in iframes with `blob:` url', async () => { @@ -651,7 +715,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('aggregates mutations', async () => { @@ -698,7 +762,7 @@ describe('record', function (this: ISuite) { ); expect(mutationEvents.length).toEqual(0); // there was no aggregate effect - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('no need for attribute mutations on adds', async () => { @@ -737,7 +801,7 @@ describe('record', function (this: ISuite) { ); expect(mutationEvents.length).toEqual(1); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); describe('loading stylesheets', () => { @@ -789,7 +853,7 @@ describe('record', function (this: ISuite) { await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`); await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures stylesheets in iframes that are still loading', async () => { @@ -823,7 +887,7 @@ describe('record', function (this: ISuite) { await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); }); @@ -849,7 +913,7 @@ describe('record', function (this: ISuite) { await ctx.page.waitForResponse(corsStylesheetURL); // wait for stylesheet to be loaded await waitForRAF(ctx.page); // wait for rrweb to emit events - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('captures adopted stylesheets in shadow doms and iframe', async () => { @@ -924,7 +988,7 @@ describe('record', function (this: ISuite) { }); await waitForRAF(ctx.page); // wait till events get sent - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); }); @@ -1025,6 +1089,6 @@ describe('record iframes', function (this: ISuite) { expect(styleRelatedEvents.length).toEqual(5); expect(addRuleCount).toEqual(2); expect(removeRuleCount).toEqual(2); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); }); diff --git a/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap b/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap new file mode 100644 index 0000000000..03526f8c0f --- /dev/null +++ b/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap @@ -0,0 +1,487 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`dialog > add dialog and show 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"text/javascript\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 5 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"I'm a dialog\\", + \\"id\\": 9 + } + ], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 10 + } + ], + \\"id\\": 6 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 6, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"non-modal\\" + }, + \\"childNodes\\": [], + \\"id\\": 11 + } + } + ] + } + } +]" +`; + +exports[`dialog > add dialog and showModal 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"text/javascript\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 5 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"I'm a dialog\\", + \\"id\\": 9 + } + ], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 10 + } + ], + \\"id\\": 6 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 6, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"modal\\" + }, + \\"childNodes\\": [], + \\"id\\": 11 + } + } + ] + } + } +]" +`; + +exports[`dialog > switch to show dialog 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"text/javascript\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 5 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"I'm a dialog\\", + \\"id\\": 9 + } + ], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 10 + } + ], + \\"id\\": 6 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 8, + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"modal\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 8, + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"non-modal\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + } +]" +`; + +exports[`dialog > switch to showModal dialog 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"text/javascript\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 5 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"dialog\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"I'm a dialog\\", + \\"id\\": 9 + } + ], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 10 + } + ], + \\"id\\": 6 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 8, + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"non-modal\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 8, + \\"attributes\\": { + \\"open\\": \\"\\", + \\"rr_open_mode\\": \\"modal\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + } +]" +`; diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index 2cdf69f006..32aa4644eb 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -53,7 +53,11 @@ async function injectRecordScript( } catch (e) { // we get this error: `Protocol error (DOM.resolveNode): Node with given id does not belong to the document` // then the page wasn't loaded yet and we try again - if (!e.message.includes('DOM.resolveNode')) throw e; + if ( + !e.message.includes('DOM.resolveNode') || + !e.message.includes('DOM.describeNode') + ) + throw e; await injectRecordScript(frame, options); return; } @@ -238,7 +242,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should map scroll events correctly', async () => { @@ -261,7 +265,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); }); @@ -289,7 +293,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record DOM node removal', async () => { @@ -301,7 +305,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record DOM attribute changes', async () => { @@ -313,7 +317,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record DOM text changes', async () => { @@ -325,7 +329,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record canvas elements', async () => { @@ -342,7 +346,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should record custom events', async () => { @@ -358,7 +362,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('captures mutations on adopted stylesheets', async () => { @@ -421,7 +425,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('captures mutations on stylesheets', async () => { @@ -476,7 +480,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); }); @@ -505,7 +509,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); }); @@ -537,7 +541,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); it('should filter out forwarded cross origin rrweb messages', async () => { @@ -564,7 +568,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); }); }); @@ -592,7 +596,7 @@ describe('same origin iframes', function (this: ISuite) { // two events (full snapshot + meta) from main frame, // and two (full snapshot + mutation) from iframe expect(events.length).toBe(4); - assertSnapshot(events); + await assertSnapshot(events); }); it('should record cross-origin iframe in same-origin iframe', async () => { @@ -616,6 +620,6 @@ describe('same origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - assertSnapshot(snapshots); + await assertSnapshot(snapshots); }); }); diff --git a/packages/rrweb/test/record/dialog.test.ts b/packages/rrweb/test/record/dialog.test.ts new file mode 100644 index 0000000000..ab6542b547 --- /dev/null +++ b/packages/rrweb/test/record/dialog.test.ts @@ -0,0 +1,229 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { vi } from 'vitest'; + +import { + assertSnapshot, + getServerURL, + ISuite, + launchPuppeteer, + startServer, + waitForRAF, +} from '../utils'; +import { + attributeMutation, + EventType, + eventWithTime, + listenerHandler, +} from '@rrweb/types'; +import { recordOptions } from '../../src/types'; + +interface IWindow extends Window { + rrweb: { + record: ( + options: recordOptions, + ) => listenerHandler | undefined; + addCustomEvent(tag: string, payload: T): void; + }; + emit: (e: eventWithTime) => undefined; +} + +const attributeMutationFactory = ( + mutation: attributeMutation['attributes'], +) => { + return { + data: { + attributes: [ + { + attributes: mutation, + }, + ], + }, + }; +}; + +describe('dialog', () => { + vi.setConfig({ testTimeout: 100_000 }); + let code: ISuite['code']; + let page: ISuite['page']; + let browser: ISuite['browser']; + let server: ISuite['server']; + let serverURL: ISuite['serverURL']; + let events: ISuite['events']; + + beforeAll(async () => { + server = await startServer(); + serverURL = getServerURL(server); + browser = await launchPuppeteer(); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.umd.cjs'); + code = fs.readFileSync(bundlePath, 'utf8'); + }); + + afterEach(async () => { + await page.close(); + }); + + afterAll(async () => { + await server.close(); + await browser.close(); + }); + + beforeEach(async () => { + page = await browser.newPage(); + page.on('console', (msg) => { + console.log(msg.text()); + }); + + await page.goto(`${serverURL}/html/dialog.html`); + await page.addScriptTag({ + path: path.resolve(__dirname, '../../dist/rrweb.umd.cjs'), + }); + await waitForRAF(page); + events = []; + + await page.exposeFunction('emit', (e: eventWithTime) => { + if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) { + return; + } + events.push(e); + }); + + page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + + await page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + emit: (window as unknown as IWindow).emit, + }); + }); + + await waitForRAF(page); + }); + + it('show dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.show(); + }); + + const lastEvent = events[events.length - 1]; + + expect(lastEvent).toMatchObject(attributeMutationFactory({ open: '' })); + // assertSnapshot(events); + }); + + it('showModal dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.showModal(); + }); + + const lastEvent = events[events.length - 1]; + + expect(lastEvent).toMatchObject( + attributeMutationFactory({ rr_open_mode: 'modal' }), + ); + }); + + it('showModal & close dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.showModal(); + }); + await waitForRAF(page); + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.close(); + }); + + const lastEvent = events[events.length - 1]; + + expect(lastEvent).toMatchObject(attributeMutationFactory({ open: null })); + }); + + it('show & close dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.show(); + }); + await waitForRAF(page); + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.close(); + }); + + const lastEvent = events[events.length - 1]; + + expect(lastEvent).toMatchObject(attributeMutationFactory({ open: null })); + }); + + it('switch to showModal dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.show(); + }); + await waitForRAF(page); + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.close(); + dialog.showModal(); + }); + + await assertSnapshot(events); + }); + + it('switch to show dialog', async () => { + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.showModal(); + }); + await waitForRAF(page); + await page.evaluate(() => { + const dialog = document.querySelector('dialog') as HTMLDialogElement; + dialog.close(); + dialog.show(); + }); + + await assertSnapshot(events); + }); + + it('add dialog and showModal', async () => { + await page.evaluate(() => { + const dialog = document.createElement('dialog') as HTMLDialogElement; + document.body.appendChild(dialog); + dialog.showModal(); + }); + await waitForRAF(page); + + await assertSnapshot(events); + }); + + it('add dialog and show', async () => { + await page.evaluate(() => { + const dialog = document.createElement('dialog') as HTMLDialogElement; + document.body.appendChild(dialog); + dialog.show(); + }); + await waitForRAF(page); + + await assertSnapshot(events); + }); + + // TODO: implement me in the future + it.skip('should record playback order with multiple dialogs opening', async () => { + await page.evaluate(() => { + const dialog1 = document.createElement('dialog') as HTMLDialogElement; + dialog1.className = 'dialog1'; + document.body.appendChild(dialog1); + const dialog2 = document.createElement('dialog') as HTMLDialogElement; + dialog1.className = 'dialog2'; + document.body.appendChild(dialog2); + dialog2.showModal(); // <== Note that dialog TWO is being triggered first + dialog1.showModal(); + }); + + await waitForRAF(page); + await assertSnapshot(events); // <== This should trigger showModal() on dialog2 first, then dialog1 + }); +}); diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index c19023732a..87999c458f 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -124,7 +124,7 @@ describe('record webgl', function (this: ISuite) { ], }, }); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('will record changes to a webgl2 canvas element', async () => { @@ -150,7 +150,7 @@ describe('record webgl', function (this: ISuite) { ], }, }); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('will record changes to a canvas element before the canvas gets added', async () => { @@ -165,7 +165,7 @@ describe('record webgl', function (this: ISuite) { await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('will record changes to a canvas element before the canvas gets added (webgl2)', async () => { @@ -188,7 +188,7 @@ describe('record webgl', function (this: ISuite) { // we need to change this await waitForRAF(ctx.page); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('will record webgl variables', async () => { @@ -203,7 +203,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('will record webgl variables in reverse order', async () => { @@ -219,7 +219,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); }); it('sets _context on canvas.getContext()', async () => { @@ -264,7 +264,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - assertSnapshot(ctx.events); + await assertSnapshot(ctx.events); expect(ctx.events.length).toEqual(5); }); @@ -312,7 +312,7 @@ describe('record webgl', function (this: ISuite) { await waitForRAF(ctx.page); // should yield a frame for each change at a max of 60fps - assertSnapshot(stripBase64(ctx.events)); + await assertSnapshot(stripBase64(ctx.events)); }); }); }); diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-closed-dialogs-show-nothing-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-closed-dialogs-show-nothing-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb34401f4292e123559cdb06cd35f8842b1c146 GIT binary patch literal 10592 zcmeHNJxjw-6g{b-wxyAXOB986aqi$y@T|9VC|KJ$zW#XbJF4QUo5$~?m8*syeA$D&`@0i;T-MTOZ_ZRu z)A^by8!fABDt8xSrV2b|3QY2tAVEMfK9C_943ta;1Dk_^fz5%osrx2B4}1iBog23c$HZdmv+h_F#1FLD;RX z0>ugW7hrU6$(zw$2?DfN8UxxZ+AGll?G^2n@PPK}zwJRT?3MRl>hVC}jW(J=$4}0F E0Mb(D%HLOWaU07rnLQrBNgph=gr1vH>p6UEM|9E=dJvrxn@7??E`_|w6 z?sxOeVLxBvH#WS1Ac!&kfX}B0Vwi~_E8JGE0#BwNw(SF#Wu#Ai_aaq1i)RS35yAWH z`FjdYG@Rb?I9c63+t*kl_-)V4D7oq8I*cog(-g$*IubKrt=`1BIL(<}Ykd5+-M3CB zYwC}=H92niI*fJ7(6OTsx8e4&!J7BpTUQco{-7{c(DiAhfGbn&Yswgyf3T}>mYL?M zG2tmg=~8duXa${5td8t1>;$}k_P3xrr^l;(5wmY^f4)}#2w`__LJahD{@a<$^wh_z z+5hg|x*|+ZWv*N9pr>}PS!SlE{&f*C(o=``B2aA5-bbfn>lA??sP0flL8C$+3@D_`Dtr|8?EUI?*y*+jCd1j++QunI zf=PX+g>Hw^ChnU0si$CXg*)6f?&q>Bu6ts!zY#yo&1~DPTnT#iclTEt!uJafon(V6 zd$kFySlnkKfQHsf)1w4G}#R5BkZ4UZ;`RO-U1q*ipf0-a9ruTmlM(fCqvZyMZ_$?OUg#zI8cuS33L`$y zqMRy~J}7G4d7*71_%YH1+iO#1pE6~);<$Bvp{0XyEOh`yI#&vffM727G6QPVWPN7}MHGalI zEuNapoUl~R=2B>br=8z28BVL@`%ep(gHO_ai84wB*RVU~ zv)M!pK8i=f%AV8$l?0QxyU25SbQ;i1)M#sZqh@g?55pjHqpvjaG-{P|I49d^m6j4U z$Es2uvvfc6et7spE&VY|{UWYW+%z-ygN@e*_?no3nx&UnxU^upN;0$z?qagnL3?C| zFZQ>47uNFJw-dCBeR&q%z2wKgd`9j*G2$fa>AiKLHbKx89Do1a_14z>McGV6h*XHv zl1CIA*QnX2_k*g#_IS&F_P2~rmVm+1nxYn421PyNOwlYcRl^cq1y21eX0%lu5WOS# z≷|;!zgP+$E-?sCE8F8hNV3W@5%B-Vm;y4##0eJysmmQ+)MJyE_{xio4bcqv`TU zAhdF!fmP;}x^U1-THnyJgGh_t-^(D-6K8q_Et2#&jYiJ0PHcHG*g*XK`;SVBi#uW) zh6|0&T_*QgFCN~uZQG6Vpt_Tv71|TZPsFwC`lQH?3(P050Lt=De;$~N72Y^hnlM5Z zMoOo+%Eye1f=>Kk5_HywdC=<)&S3duKk8%yOC4+1I7ziVRz3E$+`d^Z)27pZAn`xE2K`;Hh`zE9}95J_02@L#rAUmZyq4Et7|?LRZ66U0a?V z!c&z}p>U;rN}D6m!e~`cV?tyUzq}0CdMcWrdNQHl0L>uRArK)qWS0p`oAGnhBIe{z z5<=>WIxKKzXrG@`&*#<|C~R;_{8Uc~>0UG=Lv}L{Ggu$n{jhxDS4>v!H9u-C-`k^e z{Car>>DiA(QD1zqIe9qoC^ce&lhdvawdt~CgAcLaFL4w>xdZ#e!_6f5aI#Rzr(^(@ z(jO5|-5I}>_VgWk$Bu>}s-bn7L&H!)w_~5j>9O4z%5H+y>=UJsj+2czgZN--)e^ob zOL3nmPGod*a?G6_9j^+#<&8EwB??8#$jeKvdH{2aT?0PM4zTo0agdztYo=m=cep)w z_DB}@4326RDhVual90#cm3dTzklN0#YDlVS?&of~-=; z&Uq@vE-0scac6&hi#K=p{(o+JcvDVi#cgZ~MA#t^62Q)nAR#bD1&AMULT%DZ)`;Dx?$$5uuzDG|qC&Nm{jB zm`7iV5Z{@&!jz;-x|!5)W%*8geE8nzb^t#l1P{MHj#m(ir<(nnp_Z zr)KBm2xuf;=etLPj0BX;(1DYY(%cG z5bsuCTpu&=G{F{#vxzl0UjSB6?Dne0nf%6?B!0OUcBZnHiqR_CaZUifvU4m|?C*T7 z$e85O^knG&M6a2kKt>#h{O50t4L2tf0Yz4fXGzbkP1{)N5_`?vWpNazVxKd>szkN^ zi}DRdtCXB9`<}`mMu*fcRfs28BS^|qTYx$JTM(nKpm(z;7Wj%%3;+*KJe|@NSWcDl zaoPX|L+S{Fh-6l`lhW`4G-yJ2(HGOqU#>1u*)lyR6?X}=&Y}LEHc zpdq7E!(RzQMER#K0Z=5s{{ojB++>-2!)wN4G_-di6PXkkvLhyMkK(}T4J>p@TE9!;-Y z@$nWwZd(D5;y`0xL}2Y3wBonfx+{Xbvm722gBG?IoKWc6!PQ}UB6E%IP!ZfXZ2HeX f@X^WsAD#JJu83GgI51ua6^#GL&!=i{*!e#Itmd7) literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..f328c34b5b1b95a917d80f69c2fa391285891f97 GIT binary patch literal 12721 zcmeHNc~nzZ8h?t4wor?m7L?5;N?Vatfw0AeifI+3OjQ&JElZ{XB1D$R5?m^61sTIG zflO;FZAF4mKp>D%HLOWaU07rnLQrBNgph=gr1vH>p6UEM|9E=dJvrxn@7??E`_|w6 z?sxOeVLxBvH#WS1Ac!&kfX}B0Vwi~_E8JGE0#BwNw(SF#Wu#Ai_aaq1i)RS35yAWH z`FjdYG@Rb?I9c63+t*kl_-)V4D7oq8I*cog(-g$*IubKrt=`1BIL(<}Ykd5+-M3CB zYwC}=H92niI*fJ7(6OTsx8e4&!J7BpTUQco{-7{c(DiAhfGbn&Yswgyf3T}>mYL?M zG2tmg=~8duXa${5td8t1>;$}k_P3xrr^l;(5wmY^f4)}#2w`__LJahD{@a<$^wh_z z+5hg|x*|+ZWv*N9pr>}PS!SlE{&f*C(o=``B2aA5-bbfn>lA??sP0flL8C$+3@D_`Dtr|8?EUI?*y*+jCd1j++QunI zf=PX+g>Hw^ChnU0si$CXg*)6f?&q>Bu6ts!zY#yo&1~DPTnT#iclTEt!uJafon(V6 zd$kFySlnkKfQHsf)1w4G}#R5BkZ4UZ;`RO-U1q*ipf0-a9ruTmlM(fCqvZyMZ_$?OUg#zI8cuS33L`$y zqMRy~J}7G4d7*71_%YH1+iO#1pE6~);<$Bvp{0XyEOh`yI#&vffM727G6QPVWPN7}MHGalI zEuNapoUl~R=2B>br=8z28BVL@`%ep(gHO_ai84wB*RVU~ zv)M!pK8i=f%AV8$l?0QxyU25SbQ;i1)M#sZqh@g?55pjHqpvjaG-{P|I49d^m6j4U z$Es2uvvfc6et7spE&VY|{UWYW+%z-ygN@e*_?no3nx&UnxU^upN;0$z?qagnL3?C| zFZQ>47uNFJw-dCBeR&q%z2wKgd`9j*G2$fa>AiKLHbKx89Do1a_14z>McGV6h*XHv zl1CIA*QnX2_k*g#_IS&F_P2~rmVm+1nxYn421PyNOwlYcRl^cq1y21eX0%lu5WOS# z≷|;!zgP+$E-?sCE8F8hNV3W@5%B-Vm;y4##0eJysmmQ+)MJyE_{xio4bcqv`TU zAhdF!fmP;}x^U1-THnyJgGh_t-^(D-6K8q_Et2#&jYiJ0PHcHG*g*XK`;SVBi#uW) zh6|0&T_*QgFCN~uZQG6Vpt_Tv71|TZPsFwC`lQH?3(P050Lt=De;$~N72Y^hnlM5Z zMoOo+%Eye1f=>Kk5_HywdC=<)&S3duKk8%yOC4+1I7ziVRz3E$+`d^Z)27pZAn`xE2K`;Hh`zE9}95J_02@L#rAUmZyq4Et7|?LRZ66U0a?V z!c&z}p>U;rN}D6m!e~`cV?tyUzq}0CdMcWrdNQHl0L>uRArK)qWS0p`oAGnhBIe{z z5<=>WIxKKzXrG@`&*#<|C~R;_{8Uc~>0UG=Lv}L{Ggu$n{jhxDS4>v!H9u-C-`k^e z{Car>>DiA(QD1zqIe9qoC^ce&lhdvawdt~CgAcLaFL4w>xdZ#e!_6f5aI#Rzr(^(@ z(jO5|-5I}>_VgWk$Bu>}s-bn7L&H!)w_~5j>9O4z%5H+y>=UJsj+2czgZN--)e^ob zOL3nmPGod*a?G6_9j^+#<&8EwB??8#$jeKvdH{2aT?0PM4zTo0agdztYo=m=cep)w z_DB}@4326RDhVual90#cm3dTzklN0#YDlVS?&of~-=; z&Uq@vE-0scac6&hi#K=p{(o+JcvDVi#cgZ~MA#t^62Q)nAR#bD1&AMULT%DZ)`;Dx?$$5uuzDG|qC&Nm{jB zm`7iV5Z{@&!jz;-x|!5)W%*8geE8nzb^t#l1P{MHj#m(ir<(nnp_Z zr)KBm2xuf;=etLPj0BX;(1DYY(%cG z5bsuCTpu&=G{F{#vxzl0UjSB6?Dne0nf%6?B!0OUcBZnHiqR_CaZUifvU4m|?C*T7 z$e85O^knG&M6a2kKt>#h{O50t4L2tf0Yz4fXGzbkP1{)N5_`?vWpNazVxKd>szkN^ zi}DRdtCXB9`<}`mMu*fcRfs28BS^|qTYx$JTM(nKpm(z;7Wj%%3;+*KJe|@NSWcDl zaoPX|L+S{Fh-6l`lhW`4G-yJ2(HGOqU#>1u*)lyR6?X}=&Y}LEHc zpdq7E!(RzQMER#K0Z=5s{{ojB++>-2!)wN4G_-di6PXkkvLhyMkK(}T4J>p@TE9!;-Y z@$nWwZd(D5;y`0xL}2Y3wBonfx+{Xbvm722gBG?IoKWc6!PQ}UB6E%IP!ZfXZ2HeX f@X^WsAD#JJu83GgI51ua6^#GL&!=i{*!e#Itmd7) literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-close-dialog-again-when-open-attribute-gets-removed.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-close-dialog-again-when-open-attribute-gets-removed.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb34401f4292e123559cdb06cd35f8842b1c146 GIT binary patch literal 10592 zcmeHNJxjw-6g{b-wxyAXOB986aqi$y@T|9VC|KJ$zW#XbJF4QUo5$~?m8*syeA$D&`@0i;T-MTOZ_ZRu z)A^by8!fABDt8xSrV2b|3QY2tAVEMfK9C_943ta;1Dk_^fz5%osrx2B4}1iBog23c$HZdmv+h_F#1FLD;RX z0>ugW7hrU6$(zw$2?DfN8UxxZ+AGll?G^2n@PPK}zwJRT?3MRl>hVC}jW(J=$4}0F E0Mb(|e&6qW_ne##CIu}1!2SaSK^E`av2_oESY{!}yiHaMK#T18eSc6`r0xmuL#jJg z$`RyaWari`dyifk?c0}&(j40cC99kZKmX)&VsLhD#G3Nu9|j&QJiRxq`{YLpzc>(P ze_+2qCC>T0L$F6~-w~&!YjV$p+x@Pna_PKN^U8~MJidN|lD}hkf4_A`PFPHBp@$o} z%yP0_{AP+H5vRIoifYrYv4DUvK#`3X)9JHS1nF30@zHGk;}giD*_y2%0*4L6YBe%< zwsyic%VM_XXVrorU2RW`LIep%FFz4o+U#Z8X9@1hRMdnpW1WDvS93kK5Wy+|@z4?E12NYoeYx}Qi@}b^ zHCuo=t3Kt*%4?W>&^f}m3`6XaP!L9rMo}M=HY9I_d-eAGl^G9=Hl7;aJO^SOhJD)b z`dbV$jI}uiaYc<8TTMuQbr16}>xaLOZ4*8?X|0f!WmdEI)eE^aMWU?hD)7xf zwoNE1&S?&zH-7&bCoa>#rk3<5fZnV^z`N>)Er z(!{7e_~blyQcR_GUD>iWy5_*&zAbi(C$S@Iu{5O^WreameqsjXudGxzzcHVPb=rPb z##Dc^uDT+=j8WI0`NqW6^&g`?{N_VwR8drFdlByw&_@;p27St&fPz)*wPnZeVA$_n zV~#%~I5;>==!XhK00z)5g6{6G#9lys#FGoI9N+Upu-G%>unW1VF|NHB zweivh>kNIobWy3-F$}|MYHBdd+oXLi29XcZfniMjV9tn4R$(&g%5LkO2KJv^6>EBN{N^KbC#g-@$tG0FeaoUOa^*HF1LLI}2g=wXtpa^a(*2wU0}icK^YwgdZnkC}r*Y3*%OzDpp)m5!ft`UR zpRp%LdqZ+;9IKrI@_UY{`C%v;7#K)M8E63^XIPj@D&AP>ZPpJJ#a>(|}FOrflHaapeN_2#rRY(ApwM3)wxMBp4B}s1qZ{SoPDJCB3~7a~CWE zFZ-DF*NRI@Se5b~o0T3%WWh@8#DB8?4nU9!G+QozewCS6N z3fBfz?{7VY?%K5rFvwoPShy!3Ysq{#WQ`qM&U`szvTFsx@lGEyF>SPBMe@C?b%!J| zIZGU{bP1#0pi@hp55bPKbO_5X>eGuGt~rw~g_9W*GpzXN?DEtHPUY~COyT8Vo1r`5nMa`XMc)ip^n&(dNnJ#Mr7YLnh1+RdZ`S0iS-m`=On(Al9$4B?x!~yOK z+!c_$fy{j1%+&r5{EOob#~qHlnP|fo#{Xmo1}7+Bf%k1g2s#CaF%IKj*fqst7LQr5 zDfh3p*Kwe{M?ldyPLBG*{Tj--<_WK+XJF66dEVO-iIx-Z($|Z&Ei+z2kY#h;o~Tg# z-toY0E6VK6tc4bD4_Vf)es`dPE5mID(1Ak(LGW+_EQ7NK@*5$?BbHw}B8W{P{4c<7 zWXkw75d=>a5CqSN@GKIh#CWodCw>41c;bgAevl6E^Z-u}AP(^K08bAf4)FBg-%Ag! i{PPwi{*CcJk%-0ca}%kQH~$s{;m&QOt<`>%um1t{%+vn> literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal-in-full-snapshot.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal-in-full-snapshot.png new file mode 100644 index 0000000000000000000000000000000000000000..679ab53ab6018a241532b95e3e29e22a1d15e676 GIT binary patch literal 12505 zcmeHNYg7|S7H))t=!oF3M;$fb<1CzA$JYR&2>}EZ5fBj@A&`Kpq7WrBhKD9X;tO;% zu#QKRzzDvv1IcJe!ZU$&MsyXBgE0z1KoAj#7)S(>kgZC_neoT|b$8C``jM(sci*bI zb-#OWecdNFZ}PXDI%g_^AhrP;)^0(N$y@}nT4iGmdU~5{{Xk{G+~V(x2pi_TMv(bP zz}hui59AND1)n&#%c)Llyti}9ly&icbw6=o-9rAEGr4C@Zk6s?u-^AC&o&+{4w~fp z_ge!mLs>IgOV8MRxpqVG@sPDE=LE!Vn9o1i9Y~qlb7H6bq4-2b_pPRlBxZWL*8}F8 z@&==qV7r`2O?}YtBZ>3{m;xC19@oNh4tbJ~pDsaX>H$CbUrkzWZu{6+%r>{?A48^@ zTlT&PTm>jL3lK|l>zF;)!rbz;sWCT@1y-@<7S|pZhwIL43%H@+sK7mn84I}k#A5{> zXU$l^lL|b8iZz=8o-X34Hh!)!V*x)z;Rnz^$!WW(r58{DTt!^%5joGXlhHr0S7*gh zhlM1&v8;eKbHly_96{mzq?GWID4CE#nM1x`en~7&cw7|9bA&uXM=8t)AMH%HQxtH# zjBz3Az5aLvsY5)u4|PT8Sv_V&1*%kw!k1Q`ssTpG_2)P*k1kb&UFErhNk z>56#xXQ~SHBjjAjTRgHQeR?^}3UV0yOnC9zsaagpzvG`$9K9J`1%HrvN81P<@o}AD zTE^2eV}nC!aN6~2!47eqclh@y9*o$64mXESNfRLd@bB^;@p5L?`IsK``)^xY(K)&s z2rA4qCCF`i;^TEtg~*K%_(X38p$7cdC+3yeU6|A%7o-qU7zB1B^>abrj@S5(A@?x6wa!6vfd8VZcnbAr%_Ne zZA|x?rTKobu;_eDdU*qBu!>JgO(YPLie@g2jzCeFI{ia{YW4o^4HBs|{DD(L&_oxX zqttfQ3ATyCjE#N0O;_l;-X`~bEu{)WWSe$cERbH9`_uv$#rVxv7umwGcbcd{g)tp9 zNLy1{O5>B&X(P#0@%B3zd}4A!{e=}*T3T8Xnl7(v6PBkN#`w#(`|=1RX@;E>O;k0s zkw9kYj_KF)=AfzV2Y1^Nh^3Bcx+a!Euc9|!58nOCIi%M04|d7HBofK`)3ZapCq{Sd zYpakn)!9a=4|r22*c2WWiC_(5azzW`ySt& z9$rGLu9&%u7TxGM98}MD7xeSvo3AUo@1N~&4)HEI>EQCDa!?*sm+d6_Vrf}Nv3eO; zvX9sS%z~RpJ^1n*is}s`YSpN=i$40c)1`_t(w3?!`>qSa`YxYZiecEG{KK28^!hvx zk+todtzC85B21mdaP&xwUJ_mQQ~GFUX3OnefhCdR>(+LT(I`6doEd_maP30*Sf4A0 z2?j*ALl&ekDu!c7Mo}uqpm#~*OpL#G-+Aw!P_ZiJ5#!#;Z^5=kT27ws*(q7E zHeOYI9eMjI?Lg;oiX;Kmv{zS#mr(RK`oSKR%HQ|oInd^=+#8`{pQololC?iO=z&2! zCZs+OT^wFq6e*5V0;06xc`&3otxW1VOH3JvAP?PJgrbrI+81YOV!BGm>b;Z+?ZJJ| zqrw&meVdFv-N~yeXITs_N|so%=BPD_Dy92c!MrQ_g#ru`>hzH)rqNt`-Ch`JU;(xPiT z6-n{?)beT?>-9B?I4+nxG1?`Gtvw=~KMpoGT#;@_0HBoYy#E8=D~+XTtCVHUTfN0U z+pz=yx)Q@!`aaY!rWbtW8kfKP6}0T5c3!JqlZ#^%a$1^RiD4A!-bhmh&SiHDx8s1}sNsc;%f&Z>vaFwtLBmB=%j=Fo8f&${1=48`G;|1OSRG z{n)T&CfLB}JP(F&!tg=$@_bCYe(ZX1Ud2U=Dvoi2nAE!&?##tQL-)r$yuk!PKQu|{uW6}TD(42s-&Vh_Bf!UaZ3^1o{iThc+0 zFo)p6zv=&dh#64 zP9v?fK&)eDuQz%V$(Q|VH_Y1U0UZ@G|F?HFCw}7>7DC?(3-|Zj8ov!!2yP)JRN#;? zsR4HmCN&_R(_7x(d~p(j*k{6T0dRK>^^b!Q2P1+9CX*51SirG>W5Hwtcw8{60Z-D* zdib14+N#!fY!ev|h92|;ys4>t&KcmTAyhdY4o!xKy8x3KKI=IR2jKq{fMuFzhvE0s zHS;_gXPm<*ui7-ZIAxnN&osu9Ft(yyPI#`;388_45|?c6h&x%Zyj`&ccuh-CDi20KNTi=CjMa7}E(_;Yz3K|tC zqv&G+rB5_hkn~VM^DMfkKvz)uSU{JH=u#WqSD>3H9UGvD@&6?;9;5cb52g z!YO_;j;3g_tP1J*z9#;LAcCeka_7`={ldCC%V7<|d^0TQT*_;zwVLO;NxIwi$SaLk zrhp&y`Qf6@dw(^GUVK&}l6Kki@^*(3=CMVJ`!-SXezGKXYL!L&CYg}aHQQ!c)Mh~m zE9a=S!)JO9~k7EbJ(s~F*+2}8>kf6;B&Kg`U~$2o}&2h*MZSU#NB&f0gL`Lk*^xbvCef29}xW5 zV&Z0-6BrvXGMgS3Jl>hzUmp`%R{#8lbq9HmEH|)B1|~|TQr%OuuDg`1UTRaUxG%G^ z^17BhpTklgPu5HlMS+q;e}-UKk{oAd``K5i7Kz(^uGl8DNSfC2bbdZ?VIv#BAhdqw zo)I=_ac(H8>s@bx%M<0TYHsH`mf}GN6)`ENPH+oyAK&!%s}ovk6>SAk zo$mzHMjjP%3-foD)ovNQ6FwNyq?Cz7f-FC7flIV>zMO``FFwquO4cm;m&UZLb~@O~ zw^mMg;`6Lsr0rJxN1~B5@;?!Q5PB4fcwoj_^_Zo$eJlgdEIm>q1d8l;j2LlG;DL#a zZ-`_s3usD=`Ly&e*%8UIeB}a5+tfAxq?}lkAQK)}O2>6R(#-dY8u*%1SL}&<5_kdR z!t7$79AO+w73hnbe>4<7cDcVY$kW7qhCR$xmEbk+cuR}zlb#()jshN`KeUEg9d=dE zv}9S+v?_je{Oo9Vp<7%@FJ7zUksn^$@4c&T{X#iSGildbFJSoWqR7RzghuC%ojgOr z?Jqupu5#DR^(2TZ+oH6Z1(Ap4;)e`@5mB+15e%&3GJ3%()@`ylhmg0kym6PMg1u^l zZSw~(xOo!9Ii^@EzJU{d6BMgl9M2&#zs=a=N@gYq1<_}Tp{r&jK&fs3^O8^_Gg~jl zts^ydk^~-w(HQmU45M+ONpeU^^K|nYC*xHCaFPYIC1pd?Ld>i&2@F9xcj*lZJo4x7 z8QBXSdRlT;;T3l`Bu@Ojp;08vkKn$MJd-jM&BG-KbIDOW+(M%;5%{+Ypk7p1wvi&P z%qrhVow=)gQq!DWv@}=Z^6a~;sJ4x9!NC5x4neJ7N0$XQI64hQdQ3N~rj6aJA<+9W z4A}r~cHtn>iK>BKhIKEIrm>Us*Pae)mS&d+&p&^hi=)Iko*Hr<&x;muIa4)!W}79= zy_(GPWw_7YU+c}tpbD(K8C!j>2(FhfI{`GaBuNrV!)2BqUUP=)A}Sb)9SnDwxSF!9 zJiM=BGeqiwdO2jBHzQX7@)C=fiO;WFcU*b*2*a9(*HV+`ZxWC9R~@g@$Oo&BJ@{Ke z>_AA?0$wGq?!O&!m=O$k!<*4CUB44j)s0VXGOTIJE@IW`u~zjYQ9BSarQN`zPL|gA zmel|%u|0Cf7E9)K1M$*2UXq;+GyYh*>+>JNhH6hoH3x!me9Y_#1>dH-?x-1x@=2Cv z6$wT!+H*rn3a+j>WHDqq>3COGqttj9vKa8i=P*LLbFnro%~VA7 z7Z0ylccgE0s{*Ru0yg)qiW={{^;zW>pBy_*Y0XHUO%Zih4k0mQnx$PTh~~Nk{CM(l z@S5g{YZs&1H>V2Dt@Ul^IPnVr3A^PxhIe+^vkJlo?~K<^miaI)UA{d2m4``3oru}c zQvfCMXiGAGj`nf{)hfeX>Sd82$Sf?Q$*YSvrk)!r<5;#pdAcd4ui|LS{fbm@As?Ev z)H=U`S4Wza2V|wCC5`neIr&cH#kptR44~ILV`F1)t*1;aPbE-iAGj}0eCsmzlSkaZ z^#;Hi9b|+BxCwF}6CjwX8su|_ky-GPTG>w>Nr;%F04GOqr!8!j z|I`U{<}Zg-e(FPv&aJE&%yLK>jC$-MQX`vg=JxQ0stxkqv+B+}Wy)8F?+`2O5Xa-bxrXDUvFlT)U)!PPBarc*w zaq_~Lcv|0K_^&wg?)la0x1GnWfL926wJz8R+5w>(ynKDM>D5c43$Glu<<&|Ey-nv^)F3)Dpa{{K z0Sy_Q8Gf%Ea-uvk89_|nhu;FAln7mo0{(XmcogI)$k7#!F2vClj?N4y;iH6)pt0~< z#KLIUphW+zCxSG}Ue*gxTmKfeMoSyes=wEL8oF$S-bGV2w1N)gwNw3H!cXDZn`o01 zYViw}UHjULkG)BIVHF&)N&z@Xa-7>~k;K`I|LtT@Qkw d!v15M85HaYC>i>>nh8zkz2A3V*_45|?c6h&x%Zyj`&ccuh-CDi20KNTi=CjMa7}E(_;Yz3K|tC zqv&G+rB5_hkn~VM^DMfkKvz)uSU{JH=u#WqSD>3H9UGvD@&6?;9;5cb52g z!YO_;j;3g_tP1J*z9#;LAcCeka_7`={ldCC%V7<|d^0TQT*_;zwVLO;NxIwi$SaLk zrhp&y`Qf6@dw(^GUVK&}l6Kki@^*(3=CMVJ`!-SXezGKXYL!L&CYg}aHQQ!c)Mh~m zE9a=S!)JO9~k7EbJ(s~F*+2}8>kf6;B&Kg`U~$2o}&2h*MZSU#NB&f0gL`Lk*^xbvCef29}xW5 zV&Z0-6BrvXGMgS3Jl>hzUmp`%R{#8lbq9HmEH|)B1|~|TQr%OuuDg`1UTRaUxG%G^ z^17BhpTklgPu5HlMS+q;e}-UKk{oAd``K5i7Kz(^uGl8DNSfC2bbdZ?VIv#BAhdqw zo)I=_ac(H8>s@bx%M<0TYHsH`mf}GN6)`ENPH+oyAK&!%s}ovk6>SAk zo$mzHMjjP%3-foD)ovNQ6FwNyq?Cz7f-FC7flIV>zMO``FFwquO4cm;m&UZLb~@O~ zw^mMg;`6Lsr0rJxN1~B5@;?!Q5PB4fcwoj_^_Zo$eJlgdEIm>q1d8l;j2LlG;DL#a zZ-`_s3usD=`Ly&e*%8UIeB}a5+tfAxq?}lkAQK)}O2>6R(#-dY8u*%1SL}&<5_kdR z!t7$79AO+w73hnbe>4<7cDcVY$kW7qhCR$xmEbk+cuR}zlb#()jshN`KeUEg9d=dE zv}9S+v?_je{Oo9Vp<7%@FJ7zUksn^$@4c&T{X#iSGildbFJSoWqR7RzghuC%ojgOr z?Jqupu5#DR^(2TZ+oH6Z1(Ap4;)e`@5mB+15e%&3GJ3%()@`ylhmg0kym6PMg1u^l zZSw~(xOo!9Ii^@EzJU{d6BMgl9M2&#zs=a=N@gYq1<_}Tp{r&jK&fs3^O8^_Gg~jl zts^ydk^~-w(HQmU45M+ONpeU^^K|nYC*xHCaFPYIC1pd?Ld>i&2@F9xcj*lZJo4x7 z8QBXSdRlT;;T3l`Bu@Ojp;08vkKn$MJd-jM&BG-KbIDOW+(M%;5%{+Ypk7p1wvi&P z%qrhVow=)gQq!DWv@}=Z^6a~;sJ4x9!NC5x4neJ7N0$XQI64hQdQ3N~rj6aJA<+9W z4A}r~cHtn>iK>BKhIKEIrm>Us*Pae)mS&d+&p&^hi=)Iko*Hr<&x;muIa4)!W}79= zy_(GPWw_7YU+c}tpbD(K8C!j>2(FhfI{`GaBuNrV!)2BqUUP=)A}Sb)9SnDwxSF!9 zJiM=BGeqiwdO2jBHzQX7@)C=fiO;WFcU*b*2*a9(*HV+`ZxWC9R~@g@$Oo&BJ@{Ke z>_AA?0$wGq?!O&!m=O$k!<*4CUB44j)s0VXGOTIJE@IW`u~zjYQ9BSarQN`zPL|gA zmel|%u|0Cf7E9)K1M$*2UXq;+GyYh*>+>JNhH6hoH3x!me9Y_#1>dH-?x-1x@=2Cv z6$wT!+H*rn3a+j>WHDqq>3COGqttj9vKa8i=P*LLbFnro%~VA7 z7Z0ylccgE0s{*Ru0yg)qiW={{^;zW>pBy_*Y0XHUO%Zih4k0mQnx$PTh~~Nk{CM(l z@S5g{YZs&1H>V2Dt@Ul^IPnVr3A^PxhIe+^vkJlo?~K<^miaI)UA{d2m4``3oru}c zQvfCMXiGAGj`nf{)hfeX>Sd82$Sf?Q$*YSvrk)!r<5;#pdAcd4ui|LS{fbm@As?Ev z)H=U`S4Wza2V|wCC5`neIr&cH#kptR44~ILV`F1)t*1;aPbE-iAGj}0eCsmzlSkaZ z^#;Hi9b|+BxCwF}6CjwX8su|_ky-GPTG>w>Nr;%F04GOqr!8!j z|I`U{<}Zg-e(FPv&aJE&%yLK>jC$-MQX`vg=JxQ0stxkqv+B+}Wy)8F?+`2O5Xa-bxrXDUvFlT)U)!PPBarc*w zaq_~Lcv|0K_^&wg?)la0x1GnWfL926wJz8R+5w>(ynKDM>D5c43$Glu<<&|Ey-nv^)F3)Dpa{{K z0Sy_Q8Gf%Ea-uvk89_|nhu;FAln7mo0{(XmcogI)$k7#!F2vClj?N4y;iH6)pt0~< z#KLIUphW+zCxSG}Ue*gxTmKfeMoSyes=wEL8oF$S-bGV2w1N)gwNw3H!cXDZn`o01 zYViw}UHjULkG)BIVHF&)N&z@Xa-7>~k;K`I|LtT@Qkw d!v15M85HaYC>i>>nh8zkz2A3V*pg;wYkT6xtIG|vl zLLfY>F42NeLEevmRUo{AA_j~Ql<+8z1PBm9Leg`fGi%nG>5mzwGyb^e&po;4-m~}F z``h2%=iFR7KycAq`O!)QK{WB7?>&ehsxb(%++Ixu+!^kua|Rcsu!Ampko-3NpAcjd zg5SIQ@I}U0PgG;d%tFH?XRA1k|JVJw#m)r3&d;9=5<1i9EW&()T8Nv@zF4YN`@W=X zZwJTxA2RN)&BOVr#qGCvq++!$z)a_N>#k*->TpWlo&NDgr?G@#`GRprTd6p=LAgfZ z!yA>B_j1HiwkBIzk^krFD6v2jr~;c&@BOz1OI;*J>l;L4sefP(0(Ay$%VtD*sh7Ma zMro<{2esOz4q~#LgdjSn*Kc!EQK`ZLI6l^Ij1G$ymVuu}*P(Qyu1+r^2Hez!(qeAbZ_WPKQo$QY z%sMCqwq5E9+?<&*V#MgFa6rR2e* zym`;T7J68Fu_1Y)&Mj@{C%n0OyQ%3852{YT8D3D&>Y0GqbL)4j4cqNU8{|IT|%P#=8GURe4q?Q$AB5;qv6SJfcSq;b}$*PF4Y# zk^TmhxwkD02NA|l4@e{34P60HFj10UG4j2B$kVmnC1<^9cEO`$mPrcV1vme^9UI8W zZ@s$VSZbtb@Z?z6Bkw{#mJ)|N{oF;=Bc#|RnR&E5y`~-3;cj=5lkY3d6StnDSPwD6 z2&QhSiE-O1wv@gJb$wMpRNN{KW-iR$o=Z0-E{u33858GUr1WwkA)4b4Lo7~|sHm!j zjXcO11mYTt*x2+8$B02os>HSA91r`L?LnRI&=2oA4m7~8w>-vhwXEJ*P&L&=;T&aB zqD|e?k|m;{fSHlb^6?UG=(FE@`N?GxXQ@vp%Y<$d=qX?-kC4wb1<7tNSiC8>M zUE7pD*PnnJ&f^fN(Uh57;=n56+>{FIrX&QkBjf9sAU$v=D<=_@R zi8Z8L+{TnmT^6hY^S7TlU#1GKt9>n*vd~QO6pK6=M{EHmsN_Z8+UA>fb=3y{fu?V} z3s1Uw=KDxWM#o(Qw=qU>Gv zWQ1t2mF^f33_>nHUf0_H6j#K?WfcI%5X!DV*(=Ld>_iZ9OVFFL9r(f%4L6Kg0659O z;bs5@Z9NgZj1{Za`_;$mI!n*>RM>D!f?R25c{Lb(D2P=5o{9_JRTo2O{0BLiP50c> zr+5l^L{>4c3M0yh=yiD9n`RSe1)?r~qob{3_*kQusaPOOxx6-YGe)9&x~*nwDGuHT zYYTm%ZsrF}E14;vW&{>g+gkg#Gy+?8Yw@K}QysjI(%*SaCJ`l7O=|S08j`nL*35lL zz|6daGz9@^4I#+N#6%U6>b}nNt+d4#o_hYB#TP$p0+iPT)S_$Z+4AQ{3)m2SfH(ao z-#v5QJ(t3ioxI^Nn92yL*Y&RW>NH@*q#$m|zzm1T&iAdHlJXQ-yw#bP_w~bCcF^t4 z^cd&o&SlW|zKr0~fUi-NAhd@KjE1N_L}LnUv3uBusY!xrp$pIY@Rf z;_+5MEU_EIyLO8ed@~&V5WwqJE(r4sZ4aj%!0oMFk4_|2t&KBtPy4DpKnc>$CYYXM z6SHJ|Gl_&v&4gCFn?z^|V`Eahh|xAoX^+tA()93K11<1$U!vLK^YP7=OegDrwicXx zG9lgo^BJJP-l6r+p*H%dFf-{NtiP_864GC!R9$Sb*){s)*qWC{X^7g*bEo3gZuAPlUPx zSm49ho}#WmU4dpYQ7s|9K(?T4e^)1S@00t=2q6a3B4$yNUv^;<~c+c`6`=5^}(c{SfL3Hh{ WNin}nDi?M_AL92B_U7*)ef=|oQDxTv literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-show-the-dialog-when-open-attribute-gets-added.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-show-the-dialog-when-open-attribute-gets-added.png new file mode 100644 index 0000000000000000000000000000000000000000..a3faffd85acf2672b5411a7b05ffea0471ffe11e GIT binary patch literal 12445 zcmeHNYgAKL7QTq`P!N?Fg@T~vWebXeyb4KFRGI;TqYe}W6Dxv>pg;wYkT6xtIG|vl zLLfY>F42NeLEevmRUo{AA_j~Ql<+8z1PBm9Leg`fGi%nG>5mzwGyb^e&po;4-m~}F z``h2%=iFR7KycAq`O!)QK{WB7?>&ehsxb(%++Ixu+!^kua|Rcsu!Ampko-3NpAcjd zg5SIQ@I}U0PgG;d%tFH?XRA1k|JVJw#m)r3&d;9=5<1i9EW&()T8Nv@zF4YN`@W=X zZwJTxA2RN)&BOVr#qGCvq++!$z)a_N>#k*->TpWlo&NDgr?G@#`GRprTd6p=LAgfZ z!yA>B_j1HiwkBIzk^krFD6v2jr~;c&@BOz1OI;*J>l;L4sefP(0(Ay$%VtD*sh7Ma zMro<{2esOz4q~#LgdjSn*Kc!EQK`ZLI6l^Ij1G$ymVuu}*P(Qyu1+r^2Hez!(qeAbZ_WPKQo$QY z%sMCqwq5E9+?<&*V#MgFa6rR2e* zym`;T7J68Fu_1Y)&Mj@{C%n0OyQ%3852{YT8D3D&>Y0GqbL)4j4cqNU8{|IT|%P#=8GURe4q?Q$AB5;qv6SJfcSq;b}$*PF4Y# zk^TmhxwkD02NA|l4@e{34P60HFj10UG4j2B$kVmnC1<^9cEO`$mPrcV1vme^9UI8W zZ@s$VSZbtb@Z?z6Bkw{#mJ)|N{oF;=Bc#|RnR&E5y`~-3;cj=5lkY3d6StnDSPwD6 z2&QhSiE-O1wv@gJb$wMpRNN{KW-iR$o=Z0-E{u33858GUr1WwkA)4b4Lo7~|sHm!j zjXcO11mYTt*x2+8$B02os>HSA91r`L?LnRI&=2oA4m7~8w>-vhwXEJ*P&L&=;T&aB zqD|e?k|m;{fSHlb^6?UG=(FE@`N?GxXQ@vp%Y<$d=qX?-kC4wb1<7tNSiC8>M zUE7pD*PnnJ&f^fN(Uh57;=n56+>{FIrX&QkBjf9sAU$v=D<=_@R zi8Z8L+{TnmT^6hY^S7TlU#1GKt9>n*vd~QO6pK6=M{EHmsN_Z8+UA>fb=3y{fu?V} z3s1Uw=KDxWM#o(Qw=qU>Gv zWQ1t2mF^f33_>nHUf0_H6j#K?WfcI%5X!DV*(=Ld>_iZ9OVFFL9r(f%4L6Kg0659O z;bs5@Z9NgZj1{Za`_;$mI!n*>RM>D!f?R25c{Lb(D2P=5o{9_JRTo2O{0BLiP50c> zr+5l^L{>4c3M0yh=yiD9n`RSe1)?r~qob{3_*kQusaPOOxx6-YGe)9&x~*nwDGuHT zYYTm%ZsrF}E14;vW&{>g+gkg#Gy+?8Yw@K}QysjI(%*SaCJ`l7O=|S08j`nL*35lL zz|6daGz9@^4I#+N#6%U6>b}nNt+d4#o_hYB#TP$p0+iPT)S_$Z+4AQ{3)m2SfH(ao z-#v5QJ(t3ioxI^Nn92yL*Y&RW>NH@*q#$m|zzm1T&iAdHlJXQ-yw#bP_w~bCcF^t4 z^cd&o&SlW|zKr0~fUi-NAhd@KjE1N_L}LnUv3uBusY!xrp$pIY@Rf z;_+5MEU_EIyLO8ed@~&V5WwqJE(r4sZ4aj%!0oMFk4_|2t&KBtPy4DpKnc>$CYYXM z6SHJ|Gl_&v&4gCFn?z^|V`Eahh|xAoX^+tA()93K11<1$U!vLK^YP7=OegDrwicXx zG9lgo^BJJP-l6r+p*H%dFf-{NtiP_864GC!R9$Sb*){s)*qWC{X^7g*bEo3gZuAPlUPx zSm49ho}#WmU4dpYQ7s|9K(?T4e^)1S@00t=2q6a3B4$yNUv^;<~c+c`6`=5^}(c{SfL3Hh{ WNin}nDi?M_AL92B_U7*)ef=|oQDxTv literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/dialog.test.ts b/packages/rrweb/test/replay/dialog.test.ts new file mode 100644 index 0000000000..7fc1ab99f2 --- /dev/null +++ b/packages/rrweb/test/replay/dialog.test.ts @@ -0,0 +1,159 @@ +import * as fs from 'fs'; +import { toMatchImageSnapshot } from 'jest-image-snapshot'; +import * as path from 'path'; +import { vi } from 'vitest'; + +import dialogPlaybackEvents, { + closedFullSnapshotTime, + showIncrementalAttributeTime, + closeIncrementalAttributeTime, + showModalIncrementalAttributeTime, + showFullSnapshotTime, + showModalFullSnapshotTime, + showModalIncrementalAddTime, + switchBetweenShowModalAndShowIncrementalAttributeTime, + switchBetweenShowAndShowModalIncrementalAttributeTime, +} from '../events/dialog-playback'; +import { + fakeGoto, + getServerURL, + hideMouseAnimation, + ISuite, + launchPuppeteer, + startServer, + waitForRAF, +} from '../utils'; + +expect.extend({ toMatchImageSnapshot }); + +describe('dialog', () => { + vi.setConfig({ testTimeout: 100_000 }); + let code: ISuite['code']; + let page: ISuite['page']; + let browser: ISuite['browser']; + let server: ISuite['server']; + let serverURL: ISuite['serverURL']; + + beforeAll(async () => { + server = await startServer(); + serverURL = getServerURL(server); + browser = await launchPuppeteer(); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.umd.cjs'); + code = fs.readFileSync(bundlePath, 'utf8'); + }); + + afterEach(async () => { + await page.close(); + }); + + afterAll(async () => { + await server.close(); + await browser.close(); + }); + + beforeEach(async () => { + page = await browser.newPage(); + page.on('console', (msg) => { + console.log(msg.text()); + }); + + await fakeGoto(page, `${serverURL}/html/dialog.html`); + await page.evaluate(code); + await waitForRAF(page); + await hideMouseAnimation(page); + }); + + [ + { + name: 'show the dialog when open attribute gets added', + time: showIncrementalAttributeTime, + }, + { + name: 'should close dialog again when open attribute gets removed', + time: closeIncrementalAttributeTime, + }, + { + name: 'should open dialog with showModal', + time: showModalIncrementalAttributeTime, + }, + { + name: 'should switch between showModal and show', + time: switchBetweenShowModalAndShowIncrementalAttributeTime, + }, + { + name: 'should switch between show and showModal', + time: switchBetweenShowAndShowModalIncrementalAttributeTime, + }, + { + name: 'should open dialog with show in full snapshot', + time: showFullSnapshotTime, + }, + { + name: 'should open dialog with showModal in full snapshot', + time: showModalFullSnapshotTime, + }, + { + name: 'should add an opened dialog with showModal in incremental snapshot', + time: showModalIncrementalAddTime, + }, + { + name: 'should add an opened dialog with showModal in incremental snapshot alternative', + time: [showModalFullSnapshotTime, showModalIncrementalAddTime], + }, + ].forEach(({ name, time }) => { + [true, false].forEach((useVirtualDom) => { + it(`${name} (virtual dom: ${useVirtualDom})`, async () => { + await page.evaluate( + `let events = ${JSON.stringify(dialogPlaybackEvents)}`, + ); + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer(events, { useVirtualDom: ${useVirtualDom} }); + `); + const timeArray = Array.isArray(time) ? time : [time]; + for (let i = 0; i < timeArray.length; i++) { + await page.evaluate(` + window.replayer.pause(${timeArray[i]}); + `); + await waitForRAF(page); + } + + const frameImage = await page!.screenshot({ + fullPage: false, + }); + const defaultImageFilePrefix = + 'dialog-test-ts-test-replay-dialog-test-ts-dialog'; + const kebabCaseName = name + .replace(/ /g, '-') + .replace(/showModal/g, 'show-modal'); + const imageFileName = `${defaultImageFilePrefix}-${kebabCaseName}`; + expect(frameImage).toMatchImageSnapshot({ + customSnapshotIdentifier: imageFileName, + failureThreshold: 0.05, + failureThresholdType: 'percent', + dumpDiffToConsole: true, + storeReceivedOnFailure: true, + }); + }); + }); + }); + + it('closed dialogs show nothing', async () => { + await page.evaluate(`let events = ${JSON.stringify(dialogPlaybackEvents)}`); + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer(events); + `); + await waitForRAF(page); + + const frameImage = await page!.screenshot(); + expect(frameImage).toMatchImageSnapshot({ + failureThreshold: 0.05, + failureThresholdType: 'percent', + }); + }); + + // TODO: implement me in the future + it.skip('should trigger showModal on multiple dialogs in a specific order'); +}); diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 404357a799..3cdc05abd5 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -18,7 +18,8 @@ import inputEvents from './events/input'; import iframeEvents from './events/iframe'; import selectionEvents from './events/selection'; import shadowDomEvents from './events/shadow-dom'; -import textareaEvents from './events/bad-textarea'; +import badTextareaEvents from './events/bad-textarea'; +import badStyleEvents from './events/bad-style'; import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; import adoptedStyleSheet from './events/adopted-style-sheet'; @@ -1142,7 +1143,7 @@ describe('replayer', function () { }); it('can deal with legacy duplicate/conflicting values on textareas', async () => { - await page.evaluate(`events = ${JSON.stringify(textareaEvents)}`); + await page.evaluate(`events = ${JSON.stringify(badTextareaEvents)}`); const displayValue = await page.evaluate(` const { Replayer } = rrweb; @@ -1155,4 +1156,25 @@ describe('replayer', function () { // If the custom element is defined, the display value will be 'block'. expect(displayValue).toEqual('this value is used for replay'); }); + + it('can deal with duplicate/conflicting values on style elements', async () => { + await page.evaluate(`events = ${JSON.stringify(badStyleEvents)}`); + + const changedColors = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(1000); + // Get the color of the elements after applying the style mutation event + [ + replayer.iframe.contentWindow.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#one'), + ).color, + replayer.iframe.contentWindow.getComputedStyle( + replayer.iframe.contentDocument.querySelector('#two'), + ).color, + ]; +`); + const newColor = 'rgb(255, 255, 0)'; // yellow + expect(changedColors).toEqual([newColor, newColor]); + }); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index c7347d89c3..42c8961304 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -110,9 +110,17 @@ export function stringifySnapshots(snapshots: eventWithTime[]): string { snapshots .filter((s) => { if ( - s.type === EventType.IncrementalSnapshot && - (s.data.source === IncrementalSource.MouseMove || - s.data.source === IncrementalSource.ViewportResize) + // mouse move or viewport resize can happen on accidental user interference + // so we ignore them + (s.type === EventType.IncrementalSnapshot && + (s.data.source === IncrementalSource.MouseMove || + s.data.source === IncrementalSource.ViewportResize)) || + // ignore '[vite] connected' messages from vite + (s.type === EventType.Plugin && + s.data.plugin === 'rrweb/console@1' && + (s.data.payload as { payload: string[] })?.payload?.find((msg) => + msg.includes('[vite] connected'), + )) ) { return false; } @@ -242,18 +250,18 @@ export function stringifySnapshots(snapshots: eventWithTime[]): string { function stripBlobURLsFromAttributes(node: { attributes: { - src?: string; + [key: string]: any; }; }) { - if ( - 'src' in node.attributes && - node.attributes.src && - typeof node.attributes.src === 'string' && - node.attributes.src.startsWith('blob:') - ) { - node.attributes.src = node.attributes.src - .replace(/[\w-]+$/, '...') - .replace(/:[0-9]+\//, ':xxxx/'); + for (const attr in node.attributes) { + if ( + typeof node.attributes[attr] === 'string' && + node.attributes[attr].startsWith('blob:') + ) { + node.attributes[attr] = node.attributes[attr] + .replace(/[\w-]+$/, '...') + .replace(/:[0-9]+\//, ':xxxx/'); + } } } diff --git a/packages/rrweb/tsconfig.json b/packages/rrweb/tsconfig.json index 6a8a5ffb3d..d27cd53179 100644 --- a/packages/rrweb/tsconfig.json +++ b/packages/rrweb/tsconfig.json @@ -9,7 +9,6 @@ "vite/client", "@types/dom-mediacapture-transform", "@types/offscreencanvas", - // rrweb specific: /* * @see https://vitest.dev/config/#globals @@ -18,7 +17,6 @@ */ "vitest/globals" ], - // TODO: enable me in the future, this is quite a large project // at time of writing (April 2024) there are over 100 errors in rrweb "strict": false @@ -27,6 +25,9 @@ { "path": "../types" }, + { + "path": "../utils" + }, { "path": "../rrdom" }, diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 18210906af..b0e111d212 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,32 @@ # @rrweb/types +## 2.0.13 + +### Patch Changes + +- Merge from rrweb remote upstream + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.13 + +## 2.0.0-alpha.17 + +### Minor Changes + +- [#1503](https://github.com/rrweb-io/rrweb/pull/1503) [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158) Thanks [@Juice10](https://github.com/Juice10)! - Support top-layer components. Fixes #1381. + +### Patch Changes + +- Updated dependencies [[`40bbc25`](https://github.com/rrweb-io/rrweb/commit/40bbc25fc287badc317a53f2d3f21b1c9f2b211b), [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158), [`d350da8`](https://github.com/rrweb-io/rrweb/commit/d350da8552d8616dd118ee550bdfbce082986562), [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414)]: + - rrweb-snapshot@2.0.0-alpha.17 + +## 2.0.0-alpha.16 + +### Patch Changes + +- Updated dependencies [[`a2c8a1a`](https://github.com/rrweb-io/rrweb/commit/a2c8a1a37bfcf8389b280af792262c8263a979a3), [`d08624c`](https://github.com/rrweb-io/rrweb/commit/d08624cb28add386c3618a0e6607424c3f1884d8)]: + - rrweb-snapshot@2.0.0-alpha.16 + ## 2.0.12 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index 0019d485e0..f95bff061d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-types", - "version": "2.0.12", + "version": "2.0.13", "publishConfig": { "access": "public" }, @@ -10,9 +10,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "tsc -noEmit && vite build", + "build": "yarn turbo run prepublish", "check-types": "tsc -noEmit", - "prepublish": "npm run build", + "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types#readme", @@ -46,19 +46,11 @@ "package.json" ], "devDependencies": { - "vite": "^5.2.8", - "vite-plugin-dts": "^3.8.1" + "vite": "^5.3.1", + "vite-plugin-dts": "^3.9.1" }, "dependencies": { - "@changesets/cli": "^2.27.1", - "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", - "@saola.ai/rrweb-snapshot": "^2.0.12", - "browserslist": "^4.22.1", - "concurrently": "^7.1.0", - "cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "eslint": "^8.53.0", - "eslint-plugin-jest": "^27.6.0", - "turbo": "^2.0.3" + "@saola.ai/rrweb-snapshot": "^2.0.13" }, "browserslist": [ "supports es6-class" diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md new file mode 100644 index 0000000000..6fd00c059f --- /dev/null +++ b/packages/utils/CHANGELOG.md @@ -0,0 +1,13 @@ +# @rrweb/utils + +## 2.0.13 + +### Patch Changes + +- Merge from rrweb remote upstream + +## 2.0.0-alpha.17 + +### Patch Changes + +- [#1509](https://github.com/rrweb-io/rrweb/pull/1509) [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414) Thanks [@Juice10](https://github.com/Juice10)! - Reverse monkey patch built in methods to support LWC (and other frameworks like angular which monkey patch built in methods). diff --git a/packages/utils/Readme.md b/packages/utils/Readme.md new file mode 100644 index 0000000000..2107a2f228 --- /dev/null +++ b/packages/utils/Readme.md @@ -0,0 +1,178 @@ +# @rrweb/utils + +This package contains the shared utility functions used across rrweb packages. +See the [guide](../../guide.md) for more info on rrweb. + +## Sponsors + +[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site. + +### Gold Sponsors 🥇 + +
+ +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor + +
+ +### Silver Sponsors 🥈 + +
+ +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor + +
+ +### Bronze Sponsors 🥉 + +
+ +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor + +
+ +### Backers + + + +## Core Team Members + + + + + + + + +
+ + +
Yuyz0112 +

+
+
+ + +
Yun Feng +

+
+
+ + +
eoghanmurray +

+
+
+ + +
Juice10 +
open for rrweb consulting +
+
+ +## Who's using rrweb? + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + Smart screen recording for SaaS + +
+ + The first ever UX automation tool + + + + Remote Access & Co-Browsing + + + + The open source, fullstack Monitoring Platform. + + + + Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions. + +
+ + Intercept, Modify, Record & Replay HTTP Requests. + + + + In-app bug reporting & customer feedback platform. + + + + Self-hosted website analytics with heatmaps and session recordings. + + + + Interactive product demos for small marketing teams + +
diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 0000000000..4df05440ad --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,53 @@ +{ + "name": "@saola.ai/rrweb-utils", + "version": "2.0.13", + "publishConfig": { + "access": "public" + }, + "keywords": [ + "rrweb", + "@rrweb/utils" + ], + "scripts": { + "dev": "vite build --watch", + "build": "tsc -noEmit && vite build", + "check-types": "tsc -noEmit", + "prepublish": "npm run build", + "lint": "yarn eslint src/**/*.ts" + }, + "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/utils#readme", + "bugs": { + "url": "https://github.com/rrweb-io/rrweb/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/rrweb-io/rrweb.git" + }, + "license": "MIT", + "type": "module", + "main": "./dist/rrweb-utils.umd.cjs", + "module": "./dist/rrweb-utils.js", + "unpkg": "./dist/rrweb-utils.umd.cjs", + "typings": "dist/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/rrweb-utils.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/rrweb-utils.umd.cjs" + } + } + }, + "files": [ + "dist", + "package.json" + ], + "devDependencies": { + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" + }, + "dependencies": {} +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 0000000000..b88d7f452e --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,221 @@ +type PrototypeOwner = Node | ShadowRoot | MutationObserver | Element; +type TypeofPrototypeOwner = + | typeof Node + | typeof ShadowRoot + | typeof MutationObserver + | typeof Element; + +type BasePrototypeCache = { + Node: typeof Node.prototype; + ShadowRoot: typeof ShadowRoot.prototype; + MutationObserver: typeof MutationObserver.prototype; + Element: typeof Element.prototype; +}; + +const testableAccessors = { + Node: ['childNodes', 'parentNode', 'parentElement', 'textContent'] as const, + ShadowRoot: ['host', 'styleSheets'] as const, + Element: ['shadowRoot', 'querySelector', 'querySelectorAll'] as const, + MutationObserver: [] as const, +} as const; + +const testableMethods = { + Node: ['contains', 'getRootNode'] as const, + ShadowRoot: ['getSelection'], + Element: [], + MutationObserver: ['constructor'], +} as const; + +const untaintedBasePrototype: Partial = {}; + +export function getUntaintedPrototype( + key: T, +): BasePrototypeCache[T] { + if (untaintedBasePrototype[key]) + return untaintedBasePrototype[key] as BasePrototypeCache[T]; + + const defaultObj = globalThis[key] as TypeofPrototypeOwner; + const defaultPrototype = defaultObj.prototype as BasePrototypeCache[T]; + + // use list of testable accessors to check if the prototype is tainted + const accessorNames = + key in testableAccessors ? testableAccessors[key] : undefined; + const isUntaintedAccessors = Boolean( + accessorNames && + // @ts-expect-error 2345 + accessorNames.every((accessor: keyof typeof defaultPrototype) => + Boolean( + Object.getOwnPropertyDescriptor(defaultPrototype, accessor) + ?.get?.toString() + .includes('[native code]'), + ), + ), + ); + + const methodNames = key in testableMethods ? testableMethods[key] : undefined; + const isUntaintedMethods = Boolean( + methodNames && + methodNames.every( + // @ts-expect-error 2345 + (method: keyof typeof defaultPrototype) => + typeof defaultPrototype[method] === 'function' && + defaultPrototype[method]?.toString().includes('[native code]'), + ), + ); + + if (isUntaintedAccessors && isUntaintedMethods) { + untaintedBasePrototype[key] = defaultObj.prototype as BasePrototypeCache[T]; + return defaultObj.prototype as BasePrototypeCache[T]; + } + + try { + const iframeEl = document.createElement('iframe'); + document.body.appendChild(iframeEl); + const win = iframeEl.contentWindow; + if (!win) return defaultObj.prototype as BasePrototypeCache[T]; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any + const untaintedObject = (win as any)[key] + .prototype as BasePrototypeCache[T]; + // cleanup + document.body.removeChild(iframeEl); + + if (!untaintedObject) return defaultPrototype; + + return (untaintedBasePrototype[key] = untaintedObject); + } catch { + return defaultPrototype; + } +} + +const untaintedAccessorCache: Record< + string, + (this: PrototypeOwner, ...args: unknown[]) => unknown +> = {}; + +export function getUntaintedAccessor< + K extends keyof BasePrototypeCache, + T extends keyof BasePrototypeCache[K], +>( + key: K, + instance: BasePrototypeCache[K], + accessor: T, +): BasePrototypeCache[K][T] { + const cacheKey = `${key}.${String(accessor)}`; + if (untaintedAccessorCache[cacheKey]) + return untaintedAccessorCache[cacheKey].call( + instance, + ) as BasePrototypeCache[K][T]; + + const untaintedPrototype = getUntaintedPrototype(key); + // eslint-disable-next-line @typescript-eslint/unbound-method + const untaintedAccessor = Object.getOwnPropertyDescriptor( + untaintedPrototype, + accessor, + )?.get; + + if (!untaintedAccessor) return instance[accessor]; + + untaintedAccessorCache[cacheKey] = untaintedAccessor; + + return untaintedAccessor.call(instance) as BasePrototypeCache[K][T]; +} + +type BaseMethod = ( + this: BasePrototypeCache[K], + ...args: unknown[] +) => unknown; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const untaintedMethodCache: Record> = {}; +export function getUntaintedMethod< + K extends keyof BasePrototypeCache, + T extends keyof BasePrototypeCache[K], +>( + key: K, + instance: BasePrototypeCache[K], + method: T, +): BasePrototypeCache[K][T] { + const cacheKey = `${key}.${String(method)}`; + if (untaintedMethodCache[cacheKey]) + return untaintedMethodCache[cacheKey].bind( + instance, + ) as BasePrototypeCache[K][T]; + + const untaintedPrototype = getUntaintedPrototype(key); + const untaintedMethod = untaintedPrototype[method]; + + if (typeof untaintedMethod !== 'function') return instance[method]; + + untaintedMethodCache[cacheKey] = untaintedMethod as BaseMethod; + + return untaintedMethod.bind(instance) as BasePrototypeCache[K][T]; +} + +export function childNodes(n: Node): NodeListOf { + return getUntaintedAccessor('Node', n, 'childNodes'); +} + +export function parentNode(n: Node): ParentNode | null { + return getUntaintedAccessor('Node', n, 'parentNode'); +} + +export function parentElement(n: Node): HTMLElement | null { + return getUntaintedAccessor('Node', n, 'parentElement'); +} + +export function textContent(n: Node): string | null { + return getUntaintedAccessor('Node', n, 'textContent'); +} + +export function contains(n: Node, other: Node): boolean { + return getUntaintedMethod('Node', n, 'contains')(other); +} + +export function getRootNode(n: Node): Node { + return getUntaintedMethod('Node', n, 'getRootNode')(); +} + +export function host(n: ShadowRoot): Element | null { + if (!n || !('host' in n)) return null; + return getUntaintedAccessor('ShadowRoot', n, 'host'); +} + +export function styleSheets(n: ShadowRoot): StyleSheetList { + return n.styleSheets; +} + +export function shadowRoot(n: Node): ShadowRoot | null { + if (!n || !('shadowRoot' in n)) return null; + return getUntaintedAccessor('Element', n as Element, 'shadowRoot'); +} + +export function querySelector(n: Element, selectors: string): Element | null { + return getUntaintedAccessor('Element', n, 'querySelector')(selectors); +} + +export function querySelectorAll( + n: Element, + selectors: string, +): NodeListOf { + return getUntaintedAccessor('Element', n, 'querySelectorAll')(selectors); +} + +export function mutationObserverCtor(): (typeof MutationObserver)['prototype']['constructor'] { + return getUntaintedPrototype('MutationObserver').constructor; +} + +export default { + childNodes, + parentNode, + parentElement, + textContent, + contains, + getRootNode, + host, + styleSheets, + shadowRoot, + querySelector, + querySelectorAll, + mutationObserver: mutationObserverCtor, +}; diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 0000000000..1902007d56 --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"], + "exclude": ["vite.config.ts"], + "compilerOptions": { + "rootDir": "src", + "tsBuildInfoFile": "./tsconfig.tsbuildinfo" + }, + "references": [] +} diff --git a/packages/utils/vite.config.js b/packages/utils/vite.config.js new file mode 100644 index 0000000000..854f2b9ef0 --- /dev/null +++ b/packages/utils/vite.config.js @@ -0,0 +1,4 @@ +import path from 'path'; +import config from '../../vite.config.default'; + +export default config(path.resolve(__dirname, 'src/index.ts'), 'rrwebUtils'); diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 65ef125f1a..d35d11aedb 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,35 @@ # @rrweb/web-extension +## 2.0.13 + +### Patch Changes + +- Merge from rrweb remote upstream + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.13 + - @saola.ai/rrweb@2.0.13 + +## 2.0.0-alpha.17 + +### Minor Changes + +- [#1522](https://github.com/rrweb-io/rrweb/pull/1522) [`b1f9daa`](https://github.com/rrweb-io/rrweb/commit/b1f9daaa42f007a641104613bb07f141585f9e77) Thanks [@kirankunigiri](https://github.com/kirankunigiri)! - Added session downloader for chrome extension + +### Patch Changes + +- Updated dependencies [[`40bbc25`](https://github.com/rrweb-io/rrweb/commit/40bbc25fc287badc317a53f2d3f21b1c9f2b211b), [`68076b7`](https://github.com/rrweb-io/rrweb/commit/68076b724ff19d198d4f351a05063b85e1705a8c), [`8059d96`](https://github.com/rrweb-io/rrweb/commit/8059d9695146626b102b2059a3a9b932d5f598f6), [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158), [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414)]: + - rrweb@2.0.0-alpha.17 + - rrweb-player@2.0.0-alpha.17 + +## 2.0.0-alpha.16 + +### Patch Changes + +- Updated dependencies [[`a2c8a1a`](https://github.com/rrweb-io/rrweb/commit/a2c8a1a37bfcf8389b280af792262c8263a979a3), [`d08624c`](https://github.com/rrweb-io/rrweb/commit/d08624cb28add386c3618a0e6607424c3f1884d8)]: + - rrweb@2.0.0-alpha.16 + - rrweb-player@2.0.0-alpha.16 + ## 2.0.12 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 251ff17dec..9179a294be 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@saola.ai/rrweb-web-extension", "private": true, - "version": "2.0.12", + "version": "2.0.13", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -18,13 +18,12 @@ "prepublish": "yarn build" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.12", + "@saola.ai/rrweb-types": "^2.0.13", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^4.2.1", - "cross-env": "^7.0.3", "type-fest": "^2.19.0", - "vite": "^5.2.8", + "vite": "^5.3.1", "vite-plugin-web-extension": "^4.1.3", "vite-plugin-zip-pack": "^1.2.2", "webextension-polyfill": "^0.10.0" @@ -42,7 +41,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "@saola.ai/rrweb": "^2.0.12", - "@saola.ai/rrweb-player": "^2.0.12" + "@saola.ai/rrweb": "^2.0.13", + "@saola.ai/rrweb-player": "^2.0.13" } } diff --git a/packages/web-extension/src/background/index.ts b/packages/web-extension/src/background/index.ts index 9a0a7c58b3..4ffda7827c 100644 --- a/packages/web-extension/src/background/index.ts +++ b/packages/web-extension/src/background/index.ts @@ -2,11 +2,11 @@ import Browser from 'webextension-polyfill'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import Channel from '~/utils/channel'; import { - LocalData, + type LocalData, LocalDataKey, RecorderStatus, - Settings, - SyncData, + type Settings, + type SyncData, SyncDataKey, } from '~/types'; import { pauseRecording, resumeRecording } from '~/utils/recording'; diff --git a/packages/web-extension/src/components/CircleButton.tsx b/packages/web-extension/src/components/CircleButton.tsx index abbb68e576..f114a1cb66 100644 --- a/packages/web-extension/src/components/CircleButton.tsx +++ b/packages/web-extension/src/components/CircleButton.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonProps } from '@chakra-ui/react'; +import { Button, type ButtonProps } from '@chakra-ui/react'; interface CircleButtonProps extends ButtonProps { diameter: number; diff --git a/packages/web-extension/src/components/SidebarWithHeader.tsx b/packages/web-extension/src/components/SidebarWithHeader.tsx index bcc07147ff..7895efe613 100644 --- a/packages/web-extension/src/components/SidebarWithHeader.tsx +++ b/packages/web-extension/src/components/SidebarWithHeader.tsx @@ -12,8 +12,8 @@ import { Drawer, DrawerContent, useDisclosure, - BoxProps, - FlexProps, + type BoxProps, + type FlexProps, Heading, Stack, Text, diff --git a/packages/web-extension/src/content/index.ts b/packages/web-extension/src/content/index.ts index 18b35b2a31..673f8ad577 100644 --- a/packages/web-extension/src/content/index.ts +++ b/packages/web-extension/src/content/index.ts @@ -1,16 +1,16 @@ -import Browser, { Storage } from 'webextension-polyfill'; +import Browser, { type Storage } from 'webextension-polyfill'; import { nanoid } from 'nanoid'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import { - LocalData, + type LocalData, LocalDataKey, RecorderStatus, ServiceName, - Session, - RecordStartedMessage, - RecordStoppedMessage, + type Session, + type RecordStartedMessage, + type RecordStoppedMessage, MessageName, - EmitEventMessage, + type EmitEventMessage, } from '~/types'; import Channel from '~/utils/channel'; import { isInCrossOriginIFrame } from '~/utils'; diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts index 3ac619e642..ded4a76422 100644 --- a/packages/web-extension/src/content/inject.ts +++ b/packages/web-extension/src/content/inject.ts @@ -1,7 +1,7 @@ import { record } from '@saola.ai/rrweb'; import type { recordOptions } from '@saola.ai/rrweb'; import type { eventWithTime } from '@saola.ai/rrweb-types'; -import { MessageName, RecordStartedMessage } from '~/types'; +import { MessageName, type RecordStartedMessage } from '~/types'; import { isInCrossOriginIFrame } from '~/utils'; /** diff --git a/packages/web-extension/src/pages/SessionList.tsx b/packages/web-extension/src/pages/SessionList.tsx index 8aead1834f..d285932029 100644 --- a/packages/web-extension/src/pages/SessionList.tsx +++ b/packages/web-extension/src/pages/SessionList.tsx @@ -23,15 +23,15 @@ import { useReactTable, flexRender, getCoreRowModel, - SortingState, + type SortingState, getSortedRowModel, - PaginationState, + type PaginationState, } from '@tanstack/react-table'; import { VscTriangleDown, VscTriangleUp } from 'react-icons/vsc'; import { useNavigate } from 'react-router-dom'; -import { Session, EventName } from '~/types'; +import { type Session, EventName } from '~/types'; import Channel from '~/utils/channel'; -import { deleteSessions, getAllSessions } from '~/utils/storage'; +import { deleteSessions, getAllSessions, downloadSessions } from '~/utils/storage'; import { FiChevronLeft, FiChevronRight, @@ -292,24 +292,38 @@ export function SessionList() { ))} {Object.keys(rowSelection).length > 0 && ( - + + + + )} diff --git a/packages/web-extension/src/popup/App.tsx b/packages/web-extension/src/popup/App.tsx index a6bed7b3f2..c83a03ced4 100644 --- a/packages/web-extension/src/popup/App.tsx +++ b/packages/web-extension/src/popup/App.tsx @@ -10,16 +10,13 @@ import { } from '@chakra-ui/react'; import { FiSettings, FiList, FiPause, FiPlay } from 'react-icons/fi'; import Channel from '~/utils/channel'; -import { +import type { LocalData, - LocalDataKey, - RecorderStatus, - ServiceName, RecordStartedMessage, RecordStoppedMessage, Session, - EventName, } from '~/types'; +import { LocalDataKey, RecorderStatus, ServiceName, EventName } from '~/types'; import Browser from 'webextension-polyfill'; import { CircleButton } from '~/components/CircleButton'; import { Timer } from './Timer'; @@ -39,9 +36,8 @@ export function App() { void Browser.storage.local.get(LocalDataKey.recorderStatus).then((data) => { const localData = data as LocalData; if (!localData || !localData[LocalDataKey.recorderStatus]) return; - const { status, startTimestamp, pausedTimestamp } = localData[ - LocalDataKey.recorderStatus - ]; + const { status, startTimestamp, pausedTimestamp } = + localData[LocalDataKey.recorderStatus]; setStatus(status); if (startTimestamp && pausedTimestamp) setStartTime(Date.now() - pausedTimestamp + startTimestamp || 0); diff --git a/packages/web-extension/src/utils/channel.ts b/packages/web-extension/src/utils/channel.ts index 1a8e9b2a82..fc090bcc7f 100644 --- a/packages/web-extension/src/utils/channel.ts +++ b/packages/web-extension/src/utils/channel.ts @@ -1,5 +1,5 @@ import mitt from 'mitt'; -import Browser, { Runtime } from 'webextension-polyfill'; +import Browser, { type Runtime } from 'webextension-polyfill'; export type Message = EventType | ServiceType; export type EventType = { diff --git a/packages/web-extension/src/utils/recording.ts b/packages/web-extension/src/utils/recording.ts index 96f77c5da7..832c5a58fd 100644 --- a/packages/web-extension/src/utils/recording.ts +++ b/packages/web-extension/src/utils/recording.ts @@ -2,11 +2,11 @@ import Browser from 'webextension-polyfill'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import { - LocalData, + type LocalData, LocalDataKey, RecorderStatus, - RecordStartedMessage, - RecordStoppedMessage, + type RecordStartedMessage, + type RecordStoppedMessage, ServiceName, } from '~/types'; import type Channel from './channel'; diff --git a/packages/web-extension/src/utils/storage.ts b/packages/web-extension/src/utils/storage.ts index 6c4c460ac8..e5fc05ab26 100644 --- a/packages/web-extension/src/utils/storage.ts +++ b/packages/web-extension/src/utils/storage.ts @@ -88,3 +88,22 @@ export async function deleteSessions(ids: string[]) { return Promise.all([eventTransition.done, sessionTransition.done]); }); } + +export async function downloadSessions(ids: string[]) { + for (const sessionId of ids) { + const events = await getEvents(sessionId); + const session = await getSession(sessionId); + const blob = new Blob([JSON.stringify({ session, events }, null, 2)], { + type: 'application/json', + }); + + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${session.name}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } +} diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index 0fd0602706..2ca58feab9 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -5,6 +5,8 @@ import * as path from 'path'; import type { PackageJson } from 'type-fest'; import react from '@vitejs/plugin-react'; +const emptyOutDir = !process.argv.includes('--watch'); + function useSpecialFormat( entriesToUse: string[], format: LibraryFormats, @@ -46,7 +48,7 @@ export default defineConfig({ 'dist', process.env.TARGET_BROWSER as string, ), - emptyOutDir: true, + emptyOutDir, }, // Add the webExtension plugin plugins: [ diff --git a/tsconfig.base.json b/tsconfig.base.json index 6ca3030761..d01ad45552 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -14,7 +14,7 @@ "sourceMap": true, "skipLibCheck": true, "declaration": true, - "importsNotUsedAsValues": "error", + "verbatimModuleSyntax": true, "strict": true, "removeComments": true, "noImplicitAny": true, diff --git a/turbo.json b/turbo.json index a48aa0dfc4..54bd1b278f 100644 --- a/turbo.json +++ b/turbo.json @@ -7,9 +7,10 @@ "vite.config.defaults.ts", "tsconfig.json" ], + "globalPassThroughEnv": ["PUPPETEER_HEADLESS"], "tasks": { "prepublish": { - "dependsOn": ["^prepublish"], + "dependsOn": ["^prepublish", "//#references:update"], "outputs": [ "lib/**", "es/**", @@ -20,24 +21,30 @@ ] }, "test": { - "dependsOn": ["^prepublish"], - "passThroughEnv": ["PUPPETEER_HEADLESS"] + "dependsOn": ["^prepublish"] }, "test:watch": { "persistent": true, - "passThroughEnv": ["PUPPETEER_HEADLESS"] + "cache": false }, "test:update": { - "dependsOn": ["^prepublish"], - "passThroughEnv": ["PUPPETEER_HEADLESS"] + "dependsOn": ["^prepublish"] }, "dev": { - // "dependsOn": ["^prepublish"], + "dependsOn": ["prepublish", "^prepublish"], "persistent": true, - "cache": false, - "passThroughEnv": ["CLEAR_DIST_DIR"] + "cache": false }, "lint": {}, - "check-types": {} + "check-types": { + "dependsOn": ["^prepublish"] + }, + "//#references:update": { + "inputs": ["packages/*/package.json", "packages/plugins/*/package.json"], + "outputs": [ + "packages/*/tsconfig.json", + "packages/plugins/*/tsconfig.json" + ] + } } } diff --git a/vite.config.default.ts b/vite.config.default.ts index 9bfb2fd017..4dd3cc496e 100644 --- a/vite.config.default.ts +++ b/vite.config.default.ts @@ -7,7 +7,8 @@ import { build, Format } from 'esbuild'; import { resolve } from 'path'; import { umdWrapper } from 'esbuild-plugin-umd-wrapper'; -const emptyOutDir = process.env.CLEAR_DIST_DIR !== 'false'; +// don't empty out dir if --watch flag is passed +const emptyOutDir = !process.argv.includes('--watch'); function minifyAndUMDPlugin({ name, diff --git a/yarn.lock b/yarn.lock index 56d58b1c1a..f68b2f59ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1956,11 +1956,16 @@ globby "^11.0.0" read-yaml-file "^1.1.0" -"@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13": +"@mdn/browser-compat-data@^5.2.34": version "5.5.33" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.33.tgz#c1177469bc4d39fa24c2cd3df317039e2b465b4c" integrity sha512-uO4uIBFn9D4UNyUmaueIWnE/IJhBlSJ7W1rANvDdaawhTX8CSgqUX8tj9/6a+1WjpL9Bgirf67d//S2VwDsfig== +"@mdn/browser-compat-data@^5.5.19": + version "5.6.12" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.6.12.tgz#ac3e1855c2387334bbfdb2b6249dd95c9d9c2b70" + integrity sha512-W/Km+GFczwpoimaXbtHYdjK26VHGszOEZ9EnIyLS2E65x6LEZs7r0FovR/XSkzgNau95sTxI3JfFKQFLIJE7EQ== + "@microsoft/api-extractor-model@7.28.13": version "7.28.13" resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz#96fbc52155e0d07e0eabbd9699065b77702fe33a" @@ -2255,6 +2260,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== +"@rrweb/utils@^2.0.0-alpha.17": + version "2.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/@rrweb/utils/-/utils-2.0.0-alpha.17.tgz#d13a7326af0311e0f54551e223ace987608eaed5" + integrity sha512-HCsasPERBwOS9/LQeOytO2ETKTCqRj1wORBuxiy3t41hKhmi225DdrUPiWnyDdTQm1GdVbOymMRknJVPnZaSXw== + "@rushstack/node-core-library@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz#e26854a3314b279d57e8abdb4acce7797d02f554" @@ -3552,7 +3562,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.21.10, browserslist@^4.22.1, browserslist@^4.22.2: +browserslist@^4.22.1, browserslist@^4.22.2: version "4.23.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== @@ -3562,6 +3572,16 @@ browserslist@^4.21.10, browserslist@^4.22.1, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.16" +browserslist@^4.23.0: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + dependencies: + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -3677,7 +3697,12 @@ camelcase@^7.0.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== -caniuse-lite@^1.0.30001524, caniuse-lite@^1.0.30001629: +caniuse-lite@^1.0.30001605, caniuse-lite@^1.0.30001669: + version "1.0.30001677" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz#27c2e2c637e007cfa864a16f7dfe7cde66b38b5f" + integrity sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog== + +caniuse-lite@^1.0.30001629: version "1.0.30001636" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz#b15f52d2bdb95fad32c2f53c0b68032b85188a78" integrity sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg== @@ -4098,13 +4123,6 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-env@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" - integrity sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ== - dependencies: - cross-spawn "^6.0.5" - cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -4135,17 +4153,6 @@ cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -4592,6 +4599,11 @@ electron-to-chromium@^1.4.796: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.803.tgz#cf55808a5ee12e2a2778bbe8cdc941ef87c2093b" integrity sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g== +electron-to-chromium@^1.5.41: + version "1.5.50" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz#d9ba818da7b2b5ef1f3dd32bce7046feb7e93234" + integrity sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -4789,6 +4801,11 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-goat@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-4.0.0.tgz#9424820331b510b0666b98f7873fe11ac4aa8081" @@ -4832,18 +4849,19 @@ eslint-compat-utils@^0.5.1: dependencies: semver "^7.5.4" -eslint-plugin-compat@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz#eeaf80daa1afe495c88a47e9281295acae45c0aa" - integrity sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w== +eslint-plugin-compat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-5.0.0.tgz#d09dff02397d81c9f5b1ac740ef45b39538aa21d" + integrity sha512-29KNWyFkUbNVf6TIKVe9SVCGCtHjML3HnUg9C8LG2GsXf7miAeBOgdMc1n2B5n0sHUzg1/A4IFly7Jyf1gSbgQ== dependencies: - "@mdn/browser-compat-data" "^5.3.13" + "@mdn/browser-compat-data" "^5.5.19" ast-metadata-inferer "^0.8.0" - browserslist "^4.21.10" - caniuse-lite "^1.0.30001524" + browserslist "^4.23.0" + caniuse-lite "^1.0.30001605" find-up "^5.0.0" + globals "^13.24.0" lodash.memoize "^4.1.2" - semver "^7.5.4" + semver "^7.6.0" eslint-plugin-jest@^27.6.0: version "27.9.0" @@ -5578,7 +5596,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: +globals@^13.19.0, globals@^13.24.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== @@ -5674,6 +5692,15 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== +happy-dom@^14.12.0: + version "14.12.0" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.12.0.tgz#40c748578c6ebfb707e6ae69179d6c541d8f63b3" + integrity sha512-dHcnlGFY2o2CdxfuYpqwSrBrpj/Kuzv4u4f3TU5yHW1GL24dKij4pv1BRjXnXc3uWo8qsCbToF9weaDsm/He8A== + dependencies: + entities "^4.5.0" + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -7603,11 +7630,6 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -7657,6 +7679,11 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7968,11 +7995,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -8027,6 +8049,11 @@ picocolors@^1.0.0, picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -8841,7 +8868,7 @@ semver-match@0.1.1: dependencies: semver "^5.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -9761,47 +9788,47 @@ tty-table@^4.1.5: wcwidth "^1.0.1" yargs "^17.7.1" -turbo-darwin-64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.0.4.tgz#83c7835f8ba1f7a5473487ce73cfc8d5ad523614" - integrity sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw== +turbo-darwin-64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.2.3.tgz#f0ced75ed031091e52851cbe8bb05d21a161a22b" + integrity sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg== -turbo-darwin-arm64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.0.4.tgz#046e5768e9d6b490b7108d5bef3f4a1594aca0ba" - integrity sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A== +turbo-darwin-arm64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.2.3.tgz#0b4741383ab5070d8383891a65861a8869cc7202" + integrity sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA== -turbo-linux-64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.0.4.tgz#eab8c183a11b26ddec251d62778313a495971e4f" - integrity sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg== +turbo-linux-64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.2.3.tgz#2b339db50c12bc52ce99139c156d5555717a209d" + integrity sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ== -turbo-linux-arm64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.0.4.tgz#2dcc3f1d3e56209736b2ce3d849b80e0d7116e42" - integrity sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg== +turbo-linux-arm64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.2.3.tgz#a4daf6e0872a4e2652e2d05d68ad18cee5b10e94" + integrity sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ== -turbo-windows-64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.0.4.tgz#b2440d82892c983088ed386f9126d365594fc1a5" - integrity sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA== +turbo-windows-64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.2.3.tgz#d44b3385948bd0f2ef5c2d53391f142bdd467b18" + integrity sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ== -turbo-windows-arm64@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.0.4.tgz#e943709535baf233f5b85ed35cd95dcf86815283" - integrity sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ== +turbo-windows-arm64@2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.2.3.tgz#d0625ec53f467013a6f259f87f7fc4ae8670aaa4" + integrity sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw== -turbo@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.0.4.tgz#4fb6f0bf3be905953825de0368203e849c91e412" - integrity sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw== +turbo@^2.0.4: + version "2.2.3" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.2.3.tgz#0f45612d62526c98c75da0682aa8c26b902b5e07" + integrity sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ== optionalDependencies: - turbo-darwin-64 "2.0.4" - turbo-darwin-arm64 "2.0.4" - turbo-linux-64 "2.0.4" - turbo-linux-arm64 "2.0.4" - turbo-windows-64 "2.0.4" - turbo-windows-arm64 "2.0.4" + turbo-darwin-64 "2.2.3" + turbo-darwin-arm64 "2.2.3" + turbo-linux-64 "2.2.3" + turbo-linux-arm64 "2.2.3" + turbo-windows-64 "2.2.3" + turbo-windows-arm64 "2.2.3" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -9919,20 +9946,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" - integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== - -typescript@^4.7.3, typescript@^4.9.0, typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== - -typescript@^5.0.3: - version "5.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" - integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== +typescript@5.4.2, typescript@^5.0.3, typescript@^5.4.5: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -10017,6 +10034,14 @@ update-browserslist-db@^1.0.16: escalade "^3.1.2" picocolors "^1.0.1" +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + update-notifier@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-6.0.2.tgz#a6990253dfe6d5a02bd04fbb6a61543f55026b60" @@ -10125,7 +10150,7 @@ vite-node@1.6.0: picocolors "^1.0.0" vite "^5.0.0" -vite-plugin-dts@^3.8.1: +vite-plugin-dts@^3.8.1, vite-plugin-dts@^3.9.1: version "3.9.1" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz#625ad388ec3956708ccec7960550a7b0a8e8909e" integrity sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg== @@ -10163,7 +10188,7 @@ vite-plugin-zip-pack@^1.2.2: dependencies: jszip "^3.10.1" -vite@^5.0.0, "vite@^5.0.0 || ^4.1.4", vite@^5.2.8: +vite@^5.0.0, "vite@^5.0.0 || ^4.1.4", vite@^5.2.8, vite@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.1.tgz#bb2ca6b5fd7483249d3e86b25026e27ba8a663e6" integrity sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ== @@ -10307,6 +10332,11 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -10319,6 +10349,11 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" From 775a2021f380f112765736f76d2052ca6dab7866 Mon Sep 17 00:00:00 2001 From: Shay Malchi Date: Mon, 11 Nov 2024 11:10:03 +0200 Subject: [PATCH 29/34] [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 (#28) --- .changeset/config.json | 11 - .github/workflows/ci-cd.yml.disabled | 2 +- .vscode/rrweb-monorepo.code-workspace | 8 +- README.md | 2 +- docs/recipes/optimize-storage.md | 6 +- guide.md | 1 - guide.zh_CN.md | 3 +- package.json | 18 +- packages/all/CHANGELOG.md | 22 + packages/all/package.json | 18 +- .../test/cross-origin-iframe-packer.test.ts | 2 +- packages/packer/CHANGELOG.md | 18 + packages/packer/package.json | 14 +- packages/packer/src/pack.ts | 2 +- packages/packer/src/unpack.ts | 2 +- .../CHANGELOG.md | 18 + .../package.json | 16 +- .../CHANGELOG.md | 18 + .../package.json | 16 +- .../rrweb-plugin-console-record/CHANGELOG.md | 18 + .../rrweb-plugin-console-record/package.json | 16 +- .../rrweb-plugin-console-record/src/index.ts | 12 +- .../test/__snapshots__/index.test.ts.snap | 41 +- .../test/index.test.ts | 9 +- .../rrweb-plugin-console-replay/CHANGELOG.md | 18 + .../rrweb-plugin-console-replay/package.json | 18 +- .../rrweb-plugin-console-replay/src/index.ts | 4 +- .../CHANGELOG.md | 18 + .../package.json | 16 +- .../CHANGELOG.md | 18 + .../package.json | 18 +- packages/record/CHANGELOG.md | 20 + packages/record/package.json | 16 +- packages/replay/CHANGELOG.md | 20 + packages/replay/package.json | 16 +- packages/replay/src/index.ts | 7 +- packages/rrdom-nodejs/CHANGELOG.md | 20 + packages/rrdom-nodejs/package.json | 21 +- packages/rrdom-nodejs/src/document-nodejs.ts | 4 +- packages/rrdom/CHANGELOG.md | 18 + packages/rrdom/package.json | 16 +- packages/rrdom/src/diff.ts | 38 +- packages/rrdom/src/document.ts | 27 +- packages/rrdom/src/index.ts | 6 - packages/rrdom/test/diff.test.ts | 26 - packages/rrdom/test/diff/dialog.test.ts | 112 - packages/rrdom/test/virtual-dom.test.ts | 28 - packages/rrvideo/CHANGELOG.md | 18 + packages/rrvideo/package.json | 6 +- packages/rrweb-player/CHANGELOG.md | 20 + packages/rrweb-player/package.json | 10 +- packages/rrweb-snapshot/CHANGELOG.md | 12 + packages/rrweb-snapshot/package.json | 25 +- packages/rrweb-snapshot/src/css.ts | 1012 +++++- packages/rrweb-snapshot/src/rebuild.ts | 201 +- packages/rrweb-snapshot/src/snapshot.ts | 232 +- packages/rrweb-snapshot/src/types.ts | 39 +- packages/rrweb-snapshot/src/utils.ts | 186 +- .../__snapshots__/integration.test.ts.snap | 352 +-- .../rrweb-snapshot/test/alt-css/alt-style.css | 12 - packages/rrweb-snapshot/test/css.test.ts | 445 +-- .../test/css/style-with-import.css | 1 - packages/rrweb-snapshot/test/css/style.css | 4 +- packages/rrweb-snapshot/test/html/dialog.html | 5 - .../test/html/monkey-patched-elements.html | 45 - .../html/with-style-sheet-with-import.html | 4 - .../rrweb-snapshot/test/integration.test.ts | 112 +- packages/rrweb-snapshot/test/rebuild.test.ts | 89 +- packages/rrweb-snapshot/test/snapshot.test.ts | 134 +- .../test/stringify-stylesheet.bench.ts | 37 - packages/rrweb-snapshot/test/utils.test.ts | 83 +- packages/rrweb-snapshot/tsconfig.json | 10 +- packages/rrweb-snapshot/vitest.config.ts | 2 +- packages/rrweb/CHANGELOG.md | 22 + packages/rrweb/package.json | 31 +- packages/rrweb/src/index.ts | 11 +- packages/rrweb/src/record/index.ts | 23 +- packages/rrweb/src/record/mutation.ts | 128 +- packages/rrweb/src/record/observer.ts | 71 +- .../rrweb/src/record/observers/canvas/2d.ts | 8 +- .../record/observers/canvas/canvas-manager.ts | 1 + .../src/record/observers/canvas/webgl.ts | 15 +- .../rrweb/src/record/shadow-dom-manager.ts | 12 +- .../rrweb/src/record/stylesheet-manager.ts | 2 +- packages/rrweb/src/replay/canvas/index.ts | 6 +- packages/rrweb/src/replay/canvas/webgl.ts | 2 +- packages/rrweb/src/replay/dialog/index.ts | 67 - packages/rrweb/src/replay/index.ts | 107 +- packages/rrweb/src/replay/machine.ts | 11 +- packages/rrweb/src/replay/media/index.ts | 2 +- packages/rrweb/src/replay/timer.ts | 4 +- packages/rrweb/src/utils.ts | 56 +- .../__snapshots__/integration.test.ts.snap | 2700 ++++++----------- .../test/__snapshots__/record.test.ts.snap | 152 +- packages/rrweb/test/events/bad-style.ts | 231 -- packages/rrweb/test/events/dialog-playback.ts | 458 --- packages/rrweb/test/html/dialog.html | 5 - packages/rrweb/test/html/style.html | 31 - packages/rrweb/test/integration.test.ts | 409 +-- packages/rrweb/test/record.test.ts | 102 +- .../record/__snapshots__/dialog.test.ts.snap | 487 --- .../test/record/cross-origin-iframes.test.ts | 36 +- packages/rrweb/test/record/dialog.test.ts | 229 -- packages/rrweb/test/record/webgl.test.ts | 16 +- ...log-closed-dialogs-show-nothing-1-snap.png | Bin 10592 -> 0 bytes ...al-in-incremental-snapshot-alternative.png | Bin 12721 -> 0 bytes ...ith-show-modal-in-incremental-snapshot.png | Bin 12721 -> 0 bytes ...again-when-open-attribute-gets-removed.png | Bin 10592 -> 0 bytes ...open-dialog-with-show-in-full-snapshot.png | Bin 12261 -> 0 bytes ...ialog-with-show-modal-in-full-snapshot.png | Bin 12505 -> 0 bytes ...log-should-open-dialog-with-show-modal.png | Bin 12690 -> 0 bytes ...uld-switch-between-show-and-show-modal.png | Bin 12690 -> 0 bytes ...uld-switch-between-show-modal-and-show.png | Bin 12445 -> 0 bytes ...-dialog-when-open-attribute-gets-added.png | Bin 12445 -> 0 bytes packages/rrweb/test/replay/dialog.test.ts | 159 - packages/rrweb/test/replayer.test.ts | 26 +- packages/rrweb/test/utils.ts | 34 +- packages/rrweb/tsconfig.json | 5 +- packages/types/CHANGELOG.md | 18 + packages/types/package.json | 20 +- packages/utils/CHANGELOG.md | 13 - packages/utils/Readme.md | 178 -- packages/utils/package.json | 53 - packages/utils/src/index.ts | 221 -- packages/utils/tsconfig.json | 10 - packages/utils/vite.config.js | 4 - packages/web-extension/CHANGELOG.md | 20 + packages/web-extension/package.json | 11 +- .../web-extension/src/background/index.ts | 6 +- .../src/components/CircleButton.tsx | 2 +- .../src/components/SidebarWithHeader.tsx | 4 +- packages/web-extension/src/content/index.ts | 12 +- packages/web-extension/src/content/inject.ts | 2 +- .../web-extension/src/pages/SessionList.tsx | 58 +- packages/web-extension/src/popup/App.tsx | 12 +- packages/web-extension/src/utils/channel.ts | 2 +- packages/web-extension/src/utils/recording.ts | 6 +- packages/web-extension/src/utils/storage.ts | 19 - packages/web-extension/vite.config.ts | 4 +- tsconfig.base.json | 2 +- turbo.json | 27 +- vite.config.default.ts | 3 +- yarn.lock | 368 ++- 143 files changed, 3692 insertions(+), 6936 deletions(-) delete mode 100644 packages/rrdom/test/diff/dialog.test.ts delete mode 100644 packages/rrweb-snapshot/test/alt-css/alt-style.css delete mode 100644 packages/rrweb-snapshot/test/html/dialog.html delete mode 100644 packages/rrweb-snapshot/test/html/monkey-patched-elements.html delete mode 100644 packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts delete mode 100644 packages/rrweb/src/replay/dialog/index.ts delete mode 100644 packages/rrweb/test/events/bad-style.ts delete mode 100644 packages/rrweb/test/events/dialog-playback.ts delete mode 100644 packages/rrweb/test/html/dialog.html delete mode 100644 packages/rrweb/test/html/style.html delete mode 100644 packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap delete mode 100644 packages/rrweb/test/record/dialog.test.ts delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-closed-dialogs-show-nothing-1-snap.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot-alternative.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-close-dialog-again-when-open-attribute-gets-removed.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-in-full-snapshot.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal-in-full-snapshot.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-switch-between-show-and-show-modal.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-switch-between-show-modal-and-show.png delete mode 100644 packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-show-the-dialog-when-open-attribute-gets-added.png delete mode 100644 packages/rrweb/test/replay/dialog.test.ts delete mode 100644 packages/utils/CHANGELOG.md delete mode 100644 packages/utils/Readme.md delete mode 100644 packages/utils/package.json delete mode 100644 packages/utils/src/index.ts delete mode 100644 packages/utils/tsconfig.json delete mode 100644 packages/utils/vite.config.js diff --git a/.changeset/config.json b/.changeset/config.json index e51f8aa68d..9dea67bc61 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,17 +9,6 @@ "commit": false, "fixed": [ [ - "@saola.ai/rrweb-all", - "@saola.ai/replay", - "@saola.ai/record", - "@saola.ai/rrweb-packer", - "@saola.ai/rrweb-utils", - "@saola.ai/rrweb-plugin-console-record", - "@saola.ai/rrweb-plugin-console-replay", - "@saola.ai/rrweb-plugin-sequential-id-record", - "@saola.ai/rrweb-plugin-sequential-id-replay", - "@saola.ai/rrweb-plugin-canvas-webrtc-record", - "@saola.ai/rrweb-plugin-canvas-webrtc-replay", "@saola.ai/rrweb", "@saola.ai/rrweb-snapshot", "@saola.ai/rrdom", diff --git a/.github/workflows/ci-cd.yml.disabled b/.github/workflows/ci-cd.yml.disabled index c0c7b306a3..6351b826bd 100644 --- a/.github/workflows/ci-cd.yml.disabled +++ b/.github/workflows/ci-cd.yml.disabled @@ -41,5 +41,5 @@ jobs: if: failure() with: name: image-diff - path: packages/**/__image_snapshots__/__diff_output__/*.png + path: packages/rrweb/test/*/__image_snapshots__/__diff_output__/*.png if-no-files-found: ignore diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index ecb1672def..98338cf192 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -40,10 +40,6 @@ "name": "@rrweb/types", "path": "../packages/types" }, - { - "name": "@rrweb/utils", - "path": "../packages/utils" - }, { "name": "@rrweb/packer", "path": "../packages/packer" @@ -92,14 +88,12 @@ "@rrweb/record", "@rrweb/replay", "@rrweb/types", - "@rrweb/utils", "@rrweb/packer", "@rrweb/rrweb-plugin-console-record", "@rrweb/rrweb-plugin-console-replay", "@rrweb/rrweb-plugin-sequential-id", "@rrweb/rrweb-plugin-canvas-webrtc-record", "@rrweb/rrweb-plugin-canvas-webrtc-replay" - ], - "typescript.tsdk": " rrweb monorepo/node_modules/typescript/lib" + ] } } diff --git a/README.md b/README.md index e73c2a8768..17e6b5591a 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Since we want the record and replay sides to share a strongly typed data structu 1. Fork this repository. 2. Run `yarn install` in the root to install required dependencies for all sub-packages (note: `npm install` is _not_ recommended). -3. Run `yarn build:all` to build all packages and get a stable base, then `yarn dev` in the root to get auto-building for all the sub-packages whenever you modify anything. +3. Run `yarn dev` in the root to get auto-building for all the sub-packages whenever you modify anything. 4. Navigate to one of the sub-packages (in the `packages` folder) where you'd like to make a change. 5. Patch the code and run `yarn test` to run the tests, make sure they pass before you commit anything. Add test cases in order to avoid future regression. 6. If tests are failing, but the change in output is desirable, run `yarn test:update` and carefully commit the changes in test output. diff --git a/docs/recipes/optimize-storage.md b/docs/recipes/optimize-storage.md index 9a67d10331..a50118bf89 100644 --- a/docs/recipes/optimize-storage.md +++ b/docs/recipes/optimize-storage.md @@ -49,7 +49,7 @@ rrweb.record({ rrweb.record({ emit(event) {}, sampling: { - // Configure which kinds of mouse interaction should be recorded + // Configure which kins of mouse interaction should be recorded mouseInteraction: { MouseUp: false, MouseDown: false, @@ -78,7 +78,7 @@ import { pack } from '@rrweb/packer'; rrweb.record({ emit(event) {}, - packFn: pack, + packFn: rrweb.pack, }); ``` @@ -88,7 +88,7 @@ And you need to pass packer.unpack as the `unpackFn` in replaying. import { unpack } from '@rrweb/packer'; const replayer = new rrweb.Replayer(events, { - unpackFn: unpack, + unpackFn: rrweb.unpack, }); ``` diff --git a/guide.md b/guide.md index 764e359fb4..bfdb2d14fd 100644 --- a/guide.md +++ b/guide.md @@ -47,7 +47,6 @@ Besides the `rrweb` and `@rrweb/record` packages, rrweb also provides other pack - [@rrweb/replay](packages/replay): A package for replaying rrweb sessions. - [@rrweb/packer](packages/packer): A package for packing and unpacking rrweb data. - [@rrweb/types](packages/types): Contains types shared across rrweb packages. -- [@rrweb/utils](packages/utils): Contains utility functions shared across rrweb packages. - [web-extension](packages/web-extension): A web extension for rrweb. - [rrvideo](packages/rrvideo): A package for handling video operations in rrweb. - [@rrweb/rrweb-plugin-console-record](packages/plugins/rrweb-plugin-console-record): A plugin for recording console logs. diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 4078cb2b6a..1d56998d74 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -43,8 +43,7 @@ rrweb 代码分为录制和回放两部分,大多数时候用户在被录制 - [@rrweb/record](packages/record):一个用于录制 rrweb 会话的包。 - [@rrweb/replay](packages/replay):一个用于回放 rrweb 会话的包。 - [@rrweb/packer](packages/packer):一个用于打包和解包 rrweb 数据的包。 -- [@rrweb/types](packages/types):包含 rrweb 包中共享的类型定义。 -- [@rrweb/utils](packages/utils):包含 rrweb 包中共享的工具函数。 +- [@rrweb/types](packages/types):包含 rrweb 中使用的类型定义。 - [web-extension](packages/web-extension):rrweb 的网页扩展。 - [rrvideo](packages/rrvideo):一个用于处理 rrweb 中视频操作的包。 - [@rrweb/rrweb-plugin-console-record](packages/plugins/rrweb-plugin-console-record):一个用于记录控制台日志的插件。 diff --git a/package.json b/package.json index e674515625..1fdbace462 100644 --- a/package.json +++ b/package.json @@ -30,29 +30,26 @@ "@typescript-eslint/parser": "^5.62.0", "browserslist": "^4.22.1", "concurrently": "^7.1.0", - "cross-env": "^7.0.3", "esbuild-plugin-umd-wrapper": "^2.0.0", "eslint": "^8.53.0", - "eslint-plugin-compat": "^5.0.0", + "eslint-plugin-compat": "^4.2.0", "eslint-plugin-jest": "^27.6.0", "eslint-plugin-tsdoc": "^0.2.17", - "happy-dom": "^14.12.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", "prettier": "2.8.4", - "turbo": "^2.0.4", - "typescript": "^5.4.5" + "turbo": "^2.0.3", + "typescript": "^4.9.5" }, "scripts": { - "build:all": "NODE_OPTIONS='--max-old-space-size=4096' yarn turbo run prepublish", - "references:update": "yarn workspaces-to-typescript-project-references", - "test": "yarn turbo run test --concurrency=1 --continue", + "build:all": "NODE_OPTIONS='--max-old-space-size=4096' yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references' 'yarn turbo run prepublish'", + "test": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references --check' 'yarn turbo run test --concurrency=1 --continue'", "test:watch": "yarn turbo run test:watch", "test:update": "yarn turbo run test:update", "check-types": "yarn turbo run check-types --continue", "format": "yarn prettier --write '**/*.{ts,md}'", "format:head": "git diff --name-only HEAD^ |grep '\\.ts$\\|\\.md$' |xargs yarn prettier --write", - "dev": "yarn turbo run dev --concurrency=18", + "dev": "CLEAR_DIST_DIR=false yarn turbo run dev --concurrency=17", "repl": "cd packages/rrweb && npm run repl", "live-stream": "cd packages/rrweb && yarn live-stream", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'", @@ -61,8 +58,7 @@ }, "resolutions": { "**/cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "**/@types/dom-webcodecs": "0.1.5", - "typescript": "^5.4.5" + "**/@types/dom-webcodecs": "0.1.5" }, "browserslist": [ "defaults", diff --git a/packages/all/CHANGELOG.md b/packages/all/CHANGELOG.md index c7b8963501..46a256ea14 100644 --- a/packages/all/CHANGELOG.md +++ b/packages/all/CHANGELOG.md @@ -1,5 +1,27 @@ # @rrweb/all +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-packer@2.0.15 + - @saola.ai/rrweb@2.0.15 + - @saola.ai/rrweb-types@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-packer@2.0.14 + - @saola.ai/rrweb@2.0.14 + - @saola.ai/rrweb-types@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/all/package.json b/packages/all/package.json index db0833a818..dccf2365df 100644 --- a/packages/all/package.json +++ b/packages/all/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-all", - "version": "2.0.13", + "version": "2.0.15", "publishConfig": { "access": "public" }, @@ -10,11 +10,11 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "test": "vitest run", "test:watch": "vitest watch", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/all#readme", @@ -50,15 +50,15 @@ ], "devDependencies": { "puppeteer": "^20.9.0", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", - "typescript": "^5.4.5" + "typescript": "^4.7.3" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.13", - "@saola.ai/rrweb-packer": "^2.0.13", - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb-packer": "^2.0.15", + "@saola.ai/rrweb": "^2.0.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/all/test/cross-origin-iframe-packer.test.ts b/packages/all/test/cross-origin-iframe-packer.test.ts index ed8039d35c..f5df997977 100644 --- a/packages/all/test/cross-origin-iframe-packer.test.ts +++ b/packages/all/test/cross-origin-iframe-packer.test.ts @@ -149,7 +149,7 @@ describe('cross origin iframes & packer', function (this: ISuite) { const unpackedSnapshots = packedSnapshots.map((packed) => unpack(packed), ) as eventWithTime[]; - await assertSnapshot(unpackedSnapshots); + assertSnapshot(unpackedSnapshots); }); }); }); diff --git a/packages/packer/CHANGELOG.md b/packages/packer/CHANGELOG.md index 9e037c4883..25d63a1c5c 100644 --- a/packages/packer/CHANGELOG.md +++ b/packages/packer/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/packer +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-types@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-types@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/packer/package.json b/packages/packer/package.json index 43d545c6e2..7e1029934d 100644 --- a/packages/packer/package.json +++ b/packages/packer/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-packer", - "version": "2.0.13", + "version": "2.0.15", "publishConfig": { "access": "public" }, @@ -10,11 +10,11 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "test": "vitest run", "test:watch": "vitest watch", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "bugs": { @@ -70,14 +70,14 @@ "package.json" ], "devDependencies": { - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", - "typescript": "^5.4.5" + "typescript": "^4.7.3" }, "dependencies": { "fflate": "^0.4.4", - "@saola.ai/rrweb-types": "^2.0.13" + "@saola.ai/rrweb-types": "^2.0.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/packer/src/pack.ts b/packages/packer/src/pack.ts index 218b2424b4..71bec26f53 100644 --- a/packages/packer/src/pack.ts +++ b/packages/packer/src/pack.ts @@ -1,6 +1,6 @@ import { strFromU8, strToU8, zlibSync } from 'fflate'; import type { PackFn } from '@saola.ai/rrweb-types'; -import { type eventWithTimeAndPacker, MARK } from './base'; +import { eventWithTimeAndPacker, MARK } from './base'; export const pack: PackFn = (event) => { const _e: eventWithTimeAndPacker = { diff --git a/packages/packer/src/unpack.ts b/packages/packer/src/unpack.ts index 78123bf9ed..e352b6f303 100644 --- a/packages/packer/src/unpack.ts +++ b/packages/packer/src/unpack.ts @@ -1,5 +1,5 @@ import { strFromU8, strToU8, unzlibSync } from 'fflate'; -import { type eventWithTimeAndPacker, MARK } from './base'; +import { eventWithTimeAndPacker, MARK } from './base'; import type { UnpackFn, eventWithTime } from '@saola.ai/rrweb-types'; export const unpack: UnpackFn = (raw: string) => { diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-record/CHANGELOG.md b/packages/plugins/rrweb-plugin-canvas-webrtc-record/CHANGELOG.md index 2d6e25305a..df6e342504 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-record/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-record/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-canvas-webrtc-record +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json index 0e173ad2b0..45dc2336a7 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-canvas-webrtc-record", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-canvas-webrtc-record.umd.cjs", @@ -25,9 +25,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/CHANGELOG.md b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/CHANGELOG.md index e741fc5f13..f035f8819b 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-canvas-webrtc-replay +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json index ad134fb853..5cd8278625 100644 --- a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json +++ b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-canvas-webrtc-replay", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-canvas-webrtc-replay.umd.cjs", @@ -25,9 +25,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/plugins/rrweb-plugin-console-record/CHANGELOG.md b/packages/plugins/rrweb-plugin-console-record/CHANGELOG.md index 892749f7fc..6510d791a2 100644 --- a/packages/plugins/rrweb-plugin-console-record/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-console-record/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-console-record +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-console-record/package.json b/packages/plugins/rrweb-plugin-console-record/package.json index 430dd6002f..a383b06b3f 100644 --- a/packages/plugins/rrweb-plugin-console-record/package.json +++ b/packages/plugins/rrweb-plugin-console-record/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-console-record", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-console-record.umd.cjs", @@ -27,9 +27,9 @@ "dev": "vite build --watch", "test": "vitest run", "test:watch": "vitest watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -45,14 +45,14 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", "puppeteer": "^20.9.0" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/plugins/rrweb-plugin-console-record/src/index.ts b/packages/plugins/rrweb-plugin-console-record/src/index.ts index cb38d2ab80..9516816b37 100644 --- a/packages/plugins/rrweb-plugin-console-record/src/index.ts +++ b/packages/plugins/rrweb-plugin-console-record/src/index.ts @@ -193,12 +193,6 @@ function initLogObserver( (original: (...args: Array) => void) => { return (...args: Array) => { original.apply(this, args); - - if (level === 'assert' && !!args[0]) { - // assert does not log if the first argument evaluates to true - return; - } - if (inStack) { // If we are already in a stack this means something from the following code is calling a console method // likely a proxy method called from stringify. We don't want to log this as it will cause an infinite loop @@ -209,11 +203,7 @@ function initLogObserver( const trace = ErrorStackParser.parse(new Error()) .map((stackFrame: StackFrame) => stackFrame.toString()) .splice(1); // splice(1) to omit the hijacked log function - - // assert does not log its first arg, that's only used for deciding whether to log - const argsForPayload = level === 'assert' ? args.slice(1) : args; - - const payload = argsForPayload.map((s) => + const payload = args.map((s) => stringify(s, logOptions.stringifyOptions), ); logCount++; diff --git a/packages/plugins/rrweb-plugin-console-record/test/__snapshots__/index.test.ts.snap b/packages/plugins/rrweb-plugin-console-record/test/__snapshots__/index.test.ts.snap index cd0df64e96..ad900ed916 100644 --- a/packages/plugins/rrweb-plugin-console-record/test/__snapshots__/index.test.ts.snap +++ b/packages/plugins/rrweb-plugin-console-record/test/__snapshots__/index.test.ts.snap @@ -356,10 +356,11 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"assert\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:3:15\\" + \\"__puppeteer_evaluation_script__:2:15\\" ], \\"payload\\": [ - \\"\\\\\\"should log assert\\\\\\"\\" + \\"true\\", + \\"\\\\\\"assert\\\\\\"\\" ] } } @@ -371,7 +372,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"count\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:4:15\\" + \\"__puppeteer_evaluation_script__:3:15\\" ], \\"payload\\": [ \\"\\\\\\"count\\\\\\"\\" @@ -386,7 +387,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"countReset\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:5:15\\" + \\"__puppeteer_evaluation_script__:4:15\\" ], \\"payload\\": [ \\"\\\\\\"count\\\\\\"\\" @@ -401,7 +402,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"debug\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:6:15\\" + \\"__puppeteer_evaluation_script__:5:15\\" ], \\"payload\\": [ \\"\\\\\\"debug\\\\\\"\\" @@ -416,7 +417,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"dir\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:7:15\\" + \\"__puppeteer_evaluation_script__:6:15\\" ], \\"payload\\": [ \\"\\\\\\"dir\\\\\\"\\" @@ -431,7 +432,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"dirxml\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:8:15\\" + \\"__puppeteer_evaluation_script__:7:15\\" ], \\"payload\\": [ \\"\\\\\\"dirxml\\\\\\"\\" @@ -446,7 +447,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"group\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:9:15\\" + \\"__puppeteer_evaluation_script__:8:15\\" ], \\"payload\\": [] } @@ -459,7 +460,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"groupCollapsed\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:10:15\\" + \\"__puppeteer_evaluation_script__:9:15\\" ], \\"payload\\": [] } @@ -472,7 +473,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"info\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:11:15\\" + \\"__puppeteer_evaluation_script__:10:15\\" ], \\"payload\\": [ \\"\\\\\\"info\\\\\\"\\" @@ -487,7 +488,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"log\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:12:15\\" + \\"__puppeteer_evaluation_script__:11:15\\" ], \\"payload\\": [ \\"\\\\\\"log\\\\\\"\\" @@ -502,7 +503,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"table\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:13:15\\" + \\"__puppeteer_evaluation_script__:12:15\\" ], \\"payload\\": [ \\"\\\\\\"table\\\\\\"\\" @@ -517,7 +518,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"time\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:14:15\\" + \\"__puppeteer_evaluation_script__:13:15\\" ], \\"payload\\": [] } @@ -530,7 +531,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"timeEnd\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:15:15\\" + \\"__puppeteer_evaluation_script__:14:15\\" ], \\"payload\\": [] } @@ -543,7 +544,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"timeLog\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:16:15\\" + \\"__puppeteer_evaluation_script__:15:15\\" ], \\"payload\\": [] } @@ -556,7 +557,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"trace\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:17:15\\" + \\"__puppeteer_evaluation_script__:16:15\\" ], \\"payload\\": [ \\"\\\\\\"trace\\\\\\"\\" @@ -571,7 +572,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"warn\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:18:15\\" + \\"__puppeteer_evaluation_script__:17:15\\" ], \\"payload\\": [ \\"\\\\\\"warn\\\\\\"\\" @@ -586,7 +587,7 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"clear\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:19:15\\" + \\"__puppeteer_evaluation_script__:18:15\\" ], \\"payload\\": [] } @@ -599,10 +600,10 @@ exports[`rrweb-plugin-console-record > should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"log\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:20:15\\" + \\"__puppeteer_evaluation_script__:19:15\\" ], \\"payload\\": [ - \\"\\\\\\"TypeError: a message\\\\\\\\n at __puppeteer_evaluation_script__:20:19\\\\\\\\nEnd of stack for Error object\\\\\\"\\" + \\"\\\\\\"TypeError: a message\\\\\\\\n at __puppeteer_evaluation_script__:19:19\\\\\\\\nEnd of stack for Error object\\\\\\"\\" ] } } diff --git a/packages/plugins/rrweb-plugin-console-record/test/index.test.ts b/packages/plugins/rrweb-plugin-console-record/test/index.test.ts index 00f0d244b1..6630a436c7 100644 --- a/packages/plugins/rrweb-plugin-console-record/test/index.test.ts +++ b/packages/plugins/rrweb-plugin-console-record/test/index.test.ts @@ -87,17 +87,14 @@ describe('rrweb-plugin-console-record', () => { 'window.snapshots', )) as eventWithTime[]; // The snapshots should containe 1 console log, not multiple. - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record console messages', async () => { await page.goto(`${serverUrl}test/html/log.html`); await page.evaluate(() => { - // truthy assert does not log - console.assert(0 === 0, 'should not log assert'); - // falsy assert does log - console.assert(false, 'should log assert'); + console.assert(0 === 0, 'assert'); console.count('count'); console.countReset('count'); console.debug('debug'); @@ -126,6 +123,6 @@ describe('rrweb-plugin-console-record', () => { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); diff --git a/packages/plugins/rrweb-plugin-console-replay/CHANGELOG.md b/packages/plugins/rrweb-plugin-console-replay/CHANGELOG.md index d264d26a87..eff81633c9 100644 --- a/packages/plugins/rrweb-plugin-console-replay/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-console-replay/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-console-replay +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-console-replay/package.json b/packages/plugins/rrweb-plugin-console-replay/package.json index be65602323..278d746b41 100644 --- a/packages/plugins/rrweb-plugin-console-replay/package.json +++ b/packages/plugins/rrweb-plugin-console-replay/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-console-replay", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-console-replay.umd.cjs", @@ -25,9 +25,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -43,13 +43,13 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb-plugin-console-record": "^2.0.13", - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "@saola.ai/rrweb-plugin-console-record": "^2.0.15", + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/plugins/rrweb-plugin-console-replay/src/index.ts b/packages/plugins/rrweb-plugin-console-replay/src/index.ts index 152713829e..065320c551 100644 --- a/packages/plugins/rrweb-plugin-console-replay/src/index.ts +++ b/packages/plugins/rrweb-plugin-console-replay/src/index.ts @@ -1,6 +1,6 @@ import { - type LogLevel, - type LogData, + LogLevel, + LogData, PLUGIN_NAME, } from '@saola.ai/rrweb-plugin-console-record'; import type { eventWithTime } from '@saola.ai/rrweb-types'; diff --git a/packages/plugins/rrweb-plugin-sequential-id-record/CHANGELOG.md b/packages/plugins/rrweb-plugin-sequential-id-record/CHANGELOG.md index b3c0eb59bc..e92d7e7863 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-record/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-sequential-id-record/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-sequential-id-record +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-sequential-id-record/package.json b/packages/plugins/rrweb-plugin-sequential-id-record/package.json index 65c4df5ca4..d5867821ff 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-record/package.json +++ b/packages/plugins/rrweb-plugin-sequential-id-record/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-sequential-id-record", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-sequential-id-record.umd.cjs", @@ -25,9 +25,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/plugins/rrweb-plugin-sequential-id-replay/CHANGELOG.md b/packages/plugins/rrweb-plugin-sequential-id-replay/CHANGELOG.md index c1bb348b94..90552b26ff 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-replay/CHANGELOG.md +++ b/packages/plugins/rrweb-plugin-sequential-id-replay/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/rrweb-plugin-sequential-id-replay +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/plugins/rrweb-plugin-sequential-id-replay/package.json b/packages/plugins/rrweb-plugin-sequential-id-replay/package.json index 89b120dff9..b0d5d0cd46 100644 --- a/packages/plugins/rrweb-plugin-sequential-id-replay/package.json +++ b/packages/plugins/rrweb-plugin-sequential-id-replay/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-plugin-sequential-id-replay", - "version": "2.0.13", + "version": "2.0.15", "description": "", "type": "module", "main": "./dist/rrweb-plugin-sequential-id-replay.umd.cjs", @@ -25,9 +25,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -43,13 +43,13 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@saola.ai/rrweb-plugin-sequential-id-record": "^2.0.13", - "@saola.ai/rrweb": "^2.0.13", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "@saola.ai/rrweb-plugin-sequential-id-record": "^2.0.15", + "@saola.ai/rrweb": "^2.0.15", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "peerDependencies": { - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15" } } diff --git a/packages/record/CHANGELOG.md b/packages/record/CHANGELOG.md index cb387a6097..bcee8f938c 100644 --- a/packages/record/CHANGELOG.md +++ b/packages/record/CHANGELOG.md @@ -1,5 +1,25 @@ # @rrweb/record +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + - @saola.ai/rrweb-types@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + - @saola.ai/rrweb-types@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/record/package.json b/packages/record/package.json index 58849dba2f..c5b77fe3ef 100644 --- a/packages/record/package.json +++ b/packages/record/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/record", - "version": "2.0.13", + "version": "2.0.15", "publishConfig": { "access": "public" }, @@ -10,11 +10,11 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "test": "vitest run", "test:watch": "vitest watch", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/record#readme", @@ -49,14 +49,14 @@ ], "devDependencies": { "puppeteer": "^20.9.0", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", - "typescript": "^5.4.5" + "typescript": "^4.7.3" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.13", - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb": "^2.0.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/replay/CHANGELOG.md b/packages/replay/CHANGELOG.md index 16374c7fca..4b774497df 100644 --- a/packages/replay/CHANGELOG.md +++ b/packages/replay/CHANGELOG.md @@ -1,5 +1,25 @@ # @rrweb/replay +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.15 + - @saola.ai/rrweb-types@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.14 + - @saola.ai/rrweb-types@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/replay/package.json b/packages/replay/package.json index 9c9cedf021..d3fa1f69f2 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/replay", - "version": "2.0.13", + "version": "2.0.15", "publishConfig": { "access": "public" }, @@ -10,11 +10,11 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "test": "vitest run", "test:watch": "vitest watch", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/replay#readme", @@ -50,14 +50,14 @@ ], "devDependencies": { "puppeteer": "^20.9.0", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", - "typescript": "^5.4.5" + "typescript": "^4.7.3" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.13", - "@saola.ai/rrweb": "^2.0.13" + "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb": "^2.0.15" }, "browserslist": [ "supports es6-class" diff --git a/packages/replay/src/index.ts b/packages/replay/src/index.ts index 75ccf41858..51cbb4b7db 100644 --- a/packages/replay/src/index.ts +++ b/packages/replay/src/index.ts @@ -6,9 +6,4 @@ import { } from '@saola.ai/rrweb'; import '@saola.ai/rrweb/dist/style.css'; -export { - Replayer, - type playerConfig, - type PlayerMachineState, - type SpeedMachineState, -}; +export { Replayer, playerConfig, PlayerMachineState, SpeedMachineState }; diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index 0383ed3015..5630aacda1 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,25 @@ # rrdom-nodejs +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.15 + - @saola.ai/rrdom@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.14 + - @saola.ai/rrdom@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 15f10e1552..721ba23b9c 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,13 +1,13 @@ { "name": "@saola.ai/rrdom-nodejs", - "version": "2.0.13", + "version": "2.0.15", "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", "test": "vitest run", "test:watch": "vitest watch", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "keywords": [ @@ -46,16 +46,19 @@ "compare-versions": "^4.1.3", "eslint": "^8.15.0", "puppeteer": "^9.1.1", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0", - "typescript": "^5.4.5" + "typescript": "^4.7.3" }, "dependencies": { "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "@saola.ai/rrdom": "^2.0.13", - "@saola.ai/rrweb-snapshot": "^2.0.13" - } + "@saola.ai/rrdom": "^2.0.15", + "@saola.ai/rrweb-snapshot": "^2.0.15" + }, + "browserslist": [ + "supports es6-class" + ] } diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index 35564bc39d..7821cd4188 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -12,8 +12,8 @@ import { BaseRRNode, BaseRRText, ClassList, - type IRRDocument, - type CSSStyleDeclaration, + IRRDocument, + CSSStyleDeclaration, } from '@saola.ai/rrdom'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires const nwsapi = require('nwsapi'); diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index d61490fbf0..c48509c881 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,23 @@ # rrdom +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index fcaf5ecfc7..9717c043fa 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrdom", - "version": "2.0.13", + "version": "2.0.15", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "type": "module", @@ -30,28 +30,28 @@ }, "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", "test": "vitest run", "test:watch": "vitest", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "bugs": { "url": "https://github.com/rrweb-io/rrweb/issues" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.13", + "@saola.ai/rrweb-types": "^2.0.15", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", "eslint": "^8.15.0", "puppeteer": "^17.1.3", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "typescript": "^4.9.0", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-snapshot": "^2.0.13" + "@saola.ai/rrweb-snapshot": "^2.0.15" } } diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 3994b8ec61..52c4561f1e 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -1,7 +1,7 @@ import { NodeType as RRNodeType, Mirror as NodeMirror, - type elementNode, + elementNode, } from '@saola.ai/rrweb-snapshot'; import type { canvasMutationData, @@ -21,7 +21,6 @@ import type { } from './document'; import type { RRCanvasElement, - RRDialogElement, RRElement, RRIFrameElement, RRMediaElement, @@ -286,29 +285,6 @@ function diffAfterUpdatingChildren( ); break; } - case 'DIALOG': { - const dialog = oldElement as HTMLDialogElement; - const rrDialog = newRRElement as unknown as RRDialogElement; - const wasOpen = dialog.open; - const wasModal = dialog.matches('dialog:modal'); - const shouldBeOpen = rrDialog.open; - const shouldBeModal = rrDialog.isModal; - - const modalChanged = wasModal !== shouldBeModal; - const openChanged = wasOpen !== shouldBeOpen; - - if (modalChanged || (wasOpen && openChanged)) dialog.close(); - if (shouldBeOpen && (openChanged || modalChanged)) { - try { - if (shouldBeModal) dialog.showModal(); - else dialog.show(); - } catch (e) { - console.warn(e); - } - } - - break; - } } break; } @@ -354,20 +330,12 @@ function diffProps( } }; } else if (newTree.tagName === 'IFRAME' && name === 'srcdoc') continue; - else { - try { - oldTree.setAttribute(name, newValue); - } catch (err) { - // We want to continue diffing so we quietly catch - // this exception. Otherwise, this can throw and bubble up to - // the `ReplayerEvents.Flush` listener and break rendering - console.warn(err); - } - } + else oldTree.setAttribute(name, newValue); } for (const { name } of Array.from(oldAttributes)) if (!(name in newAttributes)) oldTree.removeAttribute(name); + newTree.scrollLeft && (oldTree.scrollLeft = newTree.scrollLeft); newTree.scrollTop && (oldTree.scrollTop = newTree.scrollTop); } diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index 45ab2fede2..c82c56cea0 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -474,8 +474,7 @@ export class BaseRRElement extends BaseRRNode implements IRRElement { } public getAttribute(name: string): string | null { - if (this.attributes[name] === undefined) return null; - return this.attributes[name]; + return this.attributes[name] || null; } public setAttribute(name: string, attribute: string) { @@ -548,30 +547,6 @@ export class BaseRRMediaElement extends BaseRRElement { } } -export class BaseRRDialogElement extends BaseRRElement { - public readonly tagName = 'DIALOG' as const; - public readonly nodeName = 'DIALOG' as const; - - get isModal() { - return this.getAttribute('rr_open_mode') === 'modal'; - } - get open() { - return this.getAttribute('open') !== null; - } - public close() { - this.removeAttribute('open'); - this.removeAttribute('rr_open_mode'); - } - public show() { - this.setAttribute('open', ''); - this.setAttribute('rr_open_mode', 'non-modal'); - } - public showModal() { - this.setAttribute('open', ''); - this.setAttribute('rr_open_mode', 'modal'); - } -} - export class BaseRRText extends BaseRRNode implements IRRText { public readonly nodeType: number = NodeType.TEXT_NODE; public readonly nodeName = '#text' as const; diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index a2d6e9e73b..c1c1d0ff83 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -31,7 +31,6 @@ import { type IRRDocumentType, type IRRText, type IRRComment, - BaseRRDialogElement, } from './document'; export class RRDocument extends BaseRRDocument { @@ -105,9 +104,6 @@ export class RRDocument extends BaseRRDocument { case 'STYLE': element = new RRStyleElement(upperTagName); break; - case 'DIALOG': - element = new RRDialogElement(upperTagName); - break; default: element = new RRElement(upperTagName); break; @@ -155,8 +151,6 @@ export class RRElement extends BaseRRElement { export class RRMediaElement extends BaseRRMediaElement {} -export class RRDialogElement extends BaseRRDialogElement {} - export class RRCanvasElement extends RRElement implements IRRElement { public rr_dataURL: string | null = null; public canvasMutations: { diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index b00b66a4be..ce38bbdd2f 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -338,32 +338,6 @@ describe('diff algorithm for rrdom', () => { expect((node as Node as HTMLElement).className).toBe('node'); }); - it('ignores invalid attributes', () => { - const tagName = 'DIV'; - const node = document.createElement(tagName); - const sn = Object.assign({}, elementSn, { - attributes: { '@click': 'foo' }, - tagName, - }); - mirror.add(node, sn); - - const rrDocument = new RRDocument(); - const rrNode = rrDocument.createElement(tagName); - const sn2 = Object.assign({}, elementSn, { - attributes: { '@click': 'foo' }, - tagName, - }); - rrDocument.mirror.add(rrNode, sn2); - - rrNode.attributes = { id: 'node1', class: 'node', '@click': 'foo' }; - diff(node, rrNode, replayer); - expect((node as Node as HTMLElement).id).toBe('node1'); - expect((node as Node as HTMLElement).className).toBe('node'); - expect('@click' in (node as Node as HTMLElement)).toBe(false); - expect(warn).toHaveBeenCalledTimes(1); - warn.mockClear(); - }); - it('can update exist properties', () => { const tagName = 'DIV'; const node = document.createElement(tagName); diff --git a/packages/rrdom/test/diff/dialog.test.ts b/packages/rrdom/test/diff/dialog.test.ts deleted file mode 100644 index 11a80e6ec5..0000000000 --- a/packages/rrdom/test/diff/dialog.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @vitest-environment happy-dom - */ -import { vi, MockInstance } from 'vitest'; -import { - NodeType as RRNodeType, - createMirror, - Mirror as NodeMirror, - serializedNodeWithId, -} from 'rrweb-snapshot'; -import { RRDocument } from '../../src'; -import { diff, ReplayerHandler } from '../../src/diff'; - -describe('diff algorithm for rrdom', () => { - let mirror: NodeMirror; - let replayer: ReplayerHandler; - let warn: MockInstance; - let elementSn: serializedNodeWithId; - let elementSn2: serializedNodeWithId; - - beforeEach(() => { - mirror = createMirror(); - replayer = { - mirror, - applyCanvas: () => {}, - applyInput: () => {}, - applyScroll: () => {}, - applyStyleSheetMutation: () => {}, - afterAppend: () => {}, - }; - document.write(''); - // Mock the original console.warn function to make the test fail once console.warn is called. - warn = vi.spyOn(console, 'warn'); - - elementSn = { - type: RRNodeType.Element, - tagName: 'DIALOG', - attributes: {}, - childNodes: [], - id: 1, - }; - - elementSn2 = { - ...elementSn, - attributes: {}, - }; - }); - - afterEach(() => { - // Check that warn was not called (fail on warning) - expect(warn).not.toBeCalled(); - vi.resetAllMocks(); - }); - describe('diff dialog elements', () => { - vi.setConfig({ testTimeout: 60_000 }); - - it('should trigger `showModal` on rr_open_mode:modal attributes', () => { - const tagName = 'DIALOG'; - const node = document.createElement(tagName) as HTMLDialogElement; - vi.spyOn(node, 'matches').mockReturnValue(false); // matches is used to check if the dialog was opened with showModal - const showModalFn = vi.spyOn(node, 'showModal'); - - const rrDocument = new RRDocument(); - const rrNode = rrDocument.createElement(tagName); - rrNode.attributes = { rr_open_mode: 'modal', open: '' }; - - mirror.add(node, elementSn); - rrDocument.mirror.add(rrNode, elementSn); - diff(node, rrNode, replayer); - - expect(showModalFn).toBeCalled(); - }); - - it('should trigger `close` on rr_open_mode removed', () => { - const tagName = 'DIALOG'; - const node = document.createElement(tagName) as HTMLDialogElement; - node.showModal(); - vi.spyOn(node, 'matches').mockReturnValue(true); // matches is used to check if the dialog was opened with showModal - const closeFn = vi.spyOn(node, 'close'); - - const rrDocument = new RRDocument(); - const rrNode = rrDocument.createElement(tagName); - rrNode.attributes = {}; - - mirror.add(node, elementSn); - rrDocument.mirror.add(rrNode, elementSn); - diff(node, rrNode, replayer); - - expect(closeFn).toBeCalled(); - }); - - it('should not trigger `close` on rr_open_mode is kept', () => { - const tagName = 'DIALOG'; - const node = document.createElement(tagName) as HTMLDialogElement; - vi.spyOn(node, 'matches').mockReturnValue(true); // matches is used to check if the dialog was opened with showModal - node.setAttribute('rr_open_mode', 'modal'); - node.setAttribute('open', ''); - const closeFn = vi.spyOn(node, 'close'); - - const rrDocument = new RRDocument(); - const rrNode = rrDocument.createElement(tagName); - rrNode.attributes = { rr_open_mode: 'modal', open: '' }; - - mirror.add(node, elementSn); - rrDocument.mirror.add(rrNode, elementSn); - diff(node, rrNode, replayer); - - expect(closeFn).not.toBeCalled(); - expect(node.open).toBe(true); - }); - }); -}); diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index 3c622e81e3..d191fdda3f 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -7,7 +7,6 @@ import * as puppeteer from 'puppeteer'; import { vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { - buildNodeWithSN, cdataNode, commentNode, documentNode, @@ -208,33 +207,6 @@ describe('RRDocument for browser environment', () => { expect((rrNode as RRElement).tagName).toEqual('SHADOWROOT'); expect(rrNode).toBe(parentRRNode.shadowRoot); }); - - it('can rebuild blocked element with correct dimensions', () => { - // @ts-expect-error Testing buildNodeWithSN with rr elements - const node = buildNodeWithSN( - { - id: 1, - tagName: 'svg', - type: NodeType.Element, - isSVG: true, - attributes: { - rr_width: '50px', - rr_height: '50px', - }, - childNodes: [], - }, - { - // @ts-expect-error - doc: new RRDocument(), - mirror, - blockSelector: '*', - slimDOMOptions: {}, - }, - ) as RRElement; - - expect(node.style.width).toBe('50px'); - expect(node.style.height).toBe('50px'); - }); }); describe('create a RRDocument from a html document', () => { diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md index a6de24633f..713de82a7f 100644 --- a/packages/rrvideo/CHANGELOG.md +++ b/packages/rrvideo/CHANGELOG.md @@ -1,5 +1,23 @@ # rrvideo +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index f4a92e729e..500153d54a 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrvideo", - "version": "2.0.13", + "version": "2.0.15", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -27,13 +27,13 @@ "@types/node": "^18.15.11", "jest": "^27.5.1", "ts-jest": "^27.1.3", - "@saola.ai/rrweb-types": "^2.0.13" + "@saola.ai/rrweb-types": "^2.0.15" }, "dependencies": { "@open-tech-world/cli-progress-bar": "^2.0.2", "fs-extra": "^11.1.1", "minimist": "^1.2.5", "playwright": "^1.32.1", - "@saola.ai/rrweb-player": "^2.0.13" + "@saola.ai/rrweb-player": "^2.0.15" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 03fd79ace2..2518aab9b2 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,25 @@ # rrweb-player +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-packer@2.0.15 + - @saola.ai/replay@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-packer@2.0.14 + - @saola.ai/replay@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 8737cc5d64..82bf34b687 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,8 +1,8 @@ { "name": "@saola.ai/rrweb-player", - "version": "2.0.13", + "version": "2.0.15", "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.13", + "@saola.ai/rrweb-types": "^2.0.15", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", @@ -16,12 +16,12 @@ "svelte-preprocess": "^5.0.3", "svelte2tsx": "^0.7.6", "tslib": "^2.0.0", - "vite": "^5.3.1" + "vite": "^5.2.8" }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "@saola.ai/replay": "^2.0.13", - "@saola.ai/rrweb-packer": "^2.0.13" + "@saola.ai/replay": "^2.0.15", + "@saola.ai/rrweb-packer": "^2.0.15" }, "scripts": { "dev": "vite build --watch", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index 079cfe8f4f..7dae5eedc0 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,17 @@ # rrweb-snapshot +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + ## 2.0.13 ### Patch Changes diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index f45c60ec66..617095b932 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,20 +1,18 @@ { "name": "@saola.ai/rrweb-snapshot", - "version": "2.0.13", + "version": "2.0.15", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", "prepack": "npm run build", - "retest": "vitest run", - "test": "yarn build && vitest run", + "retest": "jest", + "test": "vitest run", "test:watch": "vitest watch", - "retest:update": "vitest run --update", - "test:update": "yarn build && vitest run --update", - "bench": "vite build && vitest bench", + "test:update": "vitest run --update", "dev": "vite build --watch", - "build": "yarn turbo prepublish -F @saola.ai/rrweb-snapshot", + "build": "yarn check-types && vite build", "check-types": "tsc --noEmit", - "prepublish": "yarn check-types && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src" }, "type": "module", @@ -54,19 +52,16 @@ }, "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-snapshot#readme", "devDependencies": { - "@rrweb/utils": "^2.0.0-alpha.17", "@types/jsdom": "^20.0.0", "@types/node": "^18.15.11", "@types/puppeteer": "^5.4.4", + "cross-env": "^5.2.0", "puppeteer": "^17.1.3", "ts-node": "^7.0.1", "tslib": "^1.9.3", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1", + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1", "vitest": "^1.4.0" - }, - "dependencies": { - "postcss": "^8.4.38" } } diff --git a/packages/rrweb-snapshot/src/css.ts b/packages/rrweb-snapshot/src/css.ts index ec85468af6..d2755a03e5 100644 --- a/packages/rrweb-snapshot/src/css.ts +++ b/packages/rrweb-snapshot/src/css.ts @@ -1,41 +1,987 @@ -import type { AcceptedPlugin, Rule } from 'postcss'; +/** + * This file is a fork of https://github.com/reworkcss/css/blob/master/lib/parse/index.js + * I fork it because: + * 1. The css library was built for node.js which does not have tree-shaking supports. + * 2. Rewrites into typescript give us a better type interface. + */ +/* eslint-disable tsdoc/syntax */ -const MEDIA_SELECTOR = /(max|min)-device-(width|height)/; -const MEDIA_SELECTOR_GLOBAL = new RegExp(MEDIA_SELECTOR.source, 'g'); +export interface ParserOptions { + /** Silently fail on parse errors */ + silent?: boolean; + /** + * The path to the file containing css. + * Makes errors and source maps more helpful, by letting them know where code comes from. + */ + source?: string; +} + +/** + * Error thrown during parsing. + */ +export interface ParserError { + /** The full error message with the source position. */ + message?: string; + /** The error message without position. */ + reason?: string; + /** The value of options.source if passed to css.parse. Otherwise undefined. */ + filename?: string; + line?: number; + column?: number; + /** The portion of code that couldn't be parsed. */ + source?: string; +} + +export interface Loc { + line?: number; + column?: number; +} + +/** + * Base AST Tree Node. + */ +export interface Node { + /** The possible values are the ones listed in the Types section on https://github.com/reworkcss/css page. */ + type?: string; + /** A reference to the parent node, or null if the node has no parent. */ + parent?: Node; + /** Information about the position in the source string that corresponds to the node. */ + position?: { + start?: Loc; + end?: Loc; + /** The value of options.source if passed to css.parse. Otherwise undefined. */ + source?: string; + /** The full source string passed to css.parse. */ + content?: string; + }; +} + +export interface NodeWithRules extends Node { + /** Array of nodes with the types rule, comment and any of the at-rule types. */ + rules: Array; +} + +export interface Rule extends Node { + /** The list of selectors of the rule, split on commas. Each selector is trimmed from whitespace and comments. */ + selectors?: string[]; + /** Array of nodes with the types declaration and comment. */ + declarations?: Array; +} + +export interface Declaration extends Node { + /** The property name, trimmed from whitespace and comments. May not be empty. */ + property?: string; + /** The value of the property, trimmed from whitespace and comments. Empty values are allowed. */ + value?: string; +} + +/** + * A rule-level or declaration-level comment. Comments inside selectors, properties and values etc. are lost. + */ +export interface Comment extends Node { + comment?: string; +} + +/** + * The @charset at-rule. + */ +export interface Charset extends Node { + /** The part following @charset. */ + charset?: string; +} + +/** + * The @custom-media at-rule + */ +export interface CustomMedia extends Node { + /** The ---prefixed name. */ + name?: string; + /** The part following the name. */ + media?: string; +} + +/** + * The @document at-rule. + */ +export interface Document extends NodeWithRules { + /** The part following @document. */ + document?: string; + /** The vendor prefix in @document, or undefined if there is none. */ + vendor?: string; +} + +/** + * The @font-face at-rule. + */ +export interface FontFace extends Node { + /** Array of nodes with the types declaration and comment. */ + declarations?: Array; +} + +/** + * The @host at-rule. + */ +export type Host = NodeWithRules; + +/** + * The @import at-rule. + */ +export interface Import extends Node { + /** The part following @import. */ + import?: string; +} + +/** + * The @keyframes at-rule. + */ +export interface KeyFrames extends Node { + /** The name of the keyframes rule. */ + name?: string; + /** The vendor prefix in @keyframes, or undefined if there is none. */ + vendor?: string; + /** Array of nodes with the types keyframe and comment. */ + keyframes?: Array; +} + +export interface KeyFrame extends Node { + /** The list of "selectors" of the keyframe rule, split on commas. Each “selector” is trimmed from whitespace. */ + values?: string[]; + /** Array of nodes with the types declaration and comment. */ + declarations?: Array; +} + +/** + * The @media at-rule. + */ +export interface Media extends NodeWithRules { + /** The part following @media. */ + media?: string; +} + +/** + * The @namespace at-rule. + */ +export interface Namespace extends Node { + /** The part following @namespace. */ + namespace?: string; +} + +/** + * The @page at-rule. + */ +export interface Page extends Node { + /** The list of selectors of the rule, split on commas. Each selector is trimmed from whitespace and comments. */ + selectors?: string[]; + /** Array of nodes with the types declaration and comment. */ + declarations?: Array; +} + +/** + * The @supports at-rule. + */ +export interface Supports extends NodeWithRules { + /** The part following @supports. */ + supports?: string; +} + +/** All at-rules. */ +export type AtRule = + | Charset + | CustomMedia + | Document + | FontFace + | Host + | Import + | KeyFrames + | Media + | Namespace + | Page + | Supports; + +/** + * A collection of rules + */ +export interface StyleRules extends NodeWithRules { + source?: string; + /** Array of Errors. Errors collected during parsing when option silent is true. */ + parsingErrors?: ParserError[]; +} + +/** + * The root node returned by css.parse. + */ +export interface Stylesheet extends Node { + stylesheet?: StyleRules; +} + +// http://www.w3.org/TR/CSS21/grammar.html +// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 +const commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; + +export function parse(css: string, options: ParserOptions = {}): Stylesheet { + /** + * Positional. + */ + + let lineno = 1; + let column = 1; + + /** + * Update lineno and column based on `str`. + */ + + function updatePosition(str: string) { + const lines = str.match(/\n/g); + if (lines) { + lineno += lines.length; + } + const i = str.lastIndexOf('\n'); + column = i === -1 ? column + str.length : str.length - i; + } + + /** + * Mark position and patch `node.position`. + */ + + function position() { + const start = { line: lineno, column }; + return ( + node: Rule | Declaration | Comment | AtRule | Stylesheet | KeyFrame, + ) => { + node.position = new Position(start); + whitespace(); + return node; + }; + } + + /** + * Store position information for a node + */ + + class Position { + public static content: string; + public content!: string; + public start!: Loc; + public end!: Loc; + public source?: string; + + constructor(start: Loc) { + this.start = start; + this.end = { line: lineno, column }; + this.source = options.source; + this.content = Position.content; + } + } + + /** + * Non-enumerable source string + */ + + Position.content = css; + + const errorsList: ParserError[] = []; + + function error(msg: string) { + const err = new Error( + `${options.source || ''}:${lineno}:${column}: ${msg}`, + ) as ParserError; + err.reason = msg; + err.filename = options.source; + err.line = lineno; + err.column = column; + err.source = css; + + if (options.silent) { + errorsList.push(err); + } else { + throw err; + } + } + + /** + * Parse stylesheet. + */ + + function stylesheet(): Stylesheet { + const rulesList = rules(); -const mediaSelectorPlugin: AcceptedPlugin = { - postcssPlugin: 'postcss-custom-selectors', - prepare() { return { - postcssPlugin: 'postcss-custom-selectors', - AtRule: function (atrule) { - if (atrule.params.match(MEDIA_SELECTOR_GLOBAL)) { - atrule.params = atrule.params.replace(MEDIA_SELECTOR_GLOBAL, '$1-$2'); - } + type: 'stylesheet', + stylesheet: { + source: options.source, + rules: rulesList, + parsingErrors: errorsList, }, }; - }, -}; - -// Simplified from https://github.com/giuseppeg/postcss-pseudo-classes/blob/master/index.js -const pseudoClassPlugin: AcceptedPlugin = { - postcssPlugin: 'postcss-hover-classes', - prepare: function () { - const fixed: Rule[] = []; - return { - Rule: function (rule) { - if (fixed.indexOf(rule) !== -1) { - return; + } + + /** + * Opening brace. + */ + + function open() { + return match(/^{\s*/); + } + + /** + * Closing brace. + */ + + function close() { + return match(/^}/); + } + + /** + * Parse ruleset. + */ + + function rules() { + let node: Rule | void; + const rules: Rule[] = []; + whitespace(); + comments(rules); + while (css.length && css.charAt(0) !== '}' && (node = atrule() || rule())) { + if (node) { + rules.push(node); + comments(rules); + } + } + return rules; + } + + /** + * Match `re` and return captures. + */ + + function match(re: RegExp) { + const m = re.exec(css); + if (!m) { + return; + } + const str = m[0]; + updatePosition(str); + css = css.slice(str.length); + return m; + } + + /** + * Parse whitespace. + */ + + function whitespace() { + match(/^\s*/); + } + + /** + * Parse comments; + */ + + function comments(rules: Rule[] = []) { + let c: Comment | void; + while ((c = comment())) { + if (c) { + rules.push(c); + } + c = comment(); + } + return rules; + } + + /** + * Parse comment. + */ + + function comment() { + const pos = position(); + if ('/' !== css.charAt(0) || '*' !== css.charAt(1)) { + return; + } + + let i = 2; + while ( + '' !== css.charAt(i) && + ('*' !== css.charAt(i) || '/' !== css.charAt(i + 1)) + ) { + ++i; + } + i += 2; + + if ('' === css.charAt(i - 1)) { + return error('End of comment missing'); + } + + const str = css.slice(2, i - 2); + column += 2; + updatePosition(str); + css = css.slice(i); + column += 2; + + return pos({ + type: 'comment', + comment: str, + }); + } + + /** + * Parse selector. + */ + + // originally from https://github.com/NxtChg/pieces/blob/3eb39c8287a97632e9347a24f333d52d916bc816/js/css_parser/css_parse.js#L46C1-L47C1 + const selectorMatcher = new RegExp( + '^((' + + [ + /[^\\]"(?:\\"|[^"])*"/.source, // consume any quoted parts (checking that the double quote isn't itself escaped) + /[^\\]'(?:\\'|[^'])*'/.source, // same but for single quotes + '[^{]', + ].join('|') + + ')+)', + ); + + function selector() { + whitespace(); + while (css[0] == '}') { + error('extra closing bracket'); + css = css.slice(1); + whitespace(); + } + + const m = match(selectorMatcher); + if (!m) { + return; + } + + /* @fix Remove all comments from selectors + * http://ostermiller.org/findcomment.html */ + const cleanedInput = m[0] + .trim() + .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') + + // Handle strings by replacing commas inside them + .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => { + return m.replace(/,/g, '\u200C'); + }); + + // Split using a custom function and restore commas in strings + return customSplit(cleanedInput).map((s) => + s.replace(/\u200C/g, ',').trim(), + ); + } + + /** + * Split selector correctly, ensuring not to split on comma if inside (). + */ + + function customSplit(input: string) { + const result = []; + let currentSegment = ''; + let depthParentheses = 0; // Track depth of parentheses + let depthBrackets = 0; // Track depth of square brackets + let currentStringChar = null; + + for (const char of input) { + const hasStringEscape = currentSegment.endsWith('\\'); + + if (currentStringChar) { + if (currentStringChar === char && !hasStringEscape) { + currentStringChar = null; } - fixed.push(rule); - rule.selectors.forEach(function (selector) { - if (selector.includes(':hover')) { - rule.selector += ',\n' + selector.replace(/:hover/g, '.\\:hover'); - } - }); - }, + } else if (char === '(') { + depthParentheses++; + } else if (char === ')') { + depthParentheses--; + } else if (char === '[') { + depthBrackets++; + } else if (char === ']') { + depthBrackets--; + } else if ('\'"'.includes(char)) { + currentStringChar = char; + } + + // Split point is a comma that is not inside parentheses or square brackets + if (char === ',' && depthParentheses === 0 && depthBrackets === 0) { + result.push(currentSegment); + currentSegment = ''; + } else { + currentSegment += char; + } + } + + // Add the last segment + if (currentSegment) { + result.push(currentSegment); + } + + return result; + } + + /** + * Parse declaration. + */ + + function declaration(): Declaration | void | never { + const pos = position(); + + // prop + // eslint-disable-next-line no-useless-escape + const propMatch = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); + if (!propMatch) { + return; + } + const prop = trim(propMatch[0]); + + // : + if (!match(/^:\s*/)) { + return error(`property missing ':'`); + } + + // val + // eslint-disable-next-line no-useless-escape + const val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/); + + const ret = pos({ + type: 'declaration', + property: prop.replace(commentre, ''), + value: val ? trim(val[0]).replace(commentre, '') : '', + }); + + // ; + match(/^[;\s]*/); + + return ret; + } + + /** + * Parse declarations. + */ + + function declarations() { + const decls: Array = []; + + if (!open()) { + return error(`missing '{'`); + } + comments(decls); + + // declarations + let decl; + while ((decl = declaration())) { + if ((decl as unknown) !== false) { + decls.push(decl); + comments(decls); + } + decl = declaration(); + } + + if (!close()) { + return error(`missing '}'`); + } + return decls; + } + + /** + * Parse keyframe. + */ + + function keyframe() { + let m; + const vals = []; + const pos = position(); + + while ((m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/))) { + vals.push(m[1]); + match(/^,\s*/); + } + + if (!vals.length) { + return; + } + + return pos({ + type: 'keyframe', + values: vals, + declarations: declarations() as Declaration[], + }); + } + + /** + * Parse keyframes. + */ + + function atkeyframes() { + const pos = position(); + let m = match(/^@([-\w]+)?keyframes\s*/); + + if (!m) { + return; + } + const vendor = m[1]; + + // identifier + m = match(/^([-\w]+)\s*/); + if (!m) { + return error('@keyframes missing name'); + } + const name = m[1]; + + if (!open()) { + return error(`@keyframes missing '{'`); + } + + let frame; + let frames = comments(); + while ((frame = keyframe())) { + frames.push(frame); + frames = frames.concat(comments()); + } + + if (!close()) { + return error(`@keyframes missing '}'`); + } + + return pos({ + type: 'keyframes', + name, + vendor, + keyframes: frames, + }); + } + + /** + * Parse supports. + */ + + function atsupports() { + const pos = position(); + const m = match(/^@supports *([^{]+)/); + + if (!m) { + return; + } + const supports = trim(m[1]); + + if (!open()) { + return error(`@supports missing '{'`); + } + + const style = comments().concat(rules()); + + if (!close()) { + return error(`@supports missing '}'`); + } + + return pos({ + type: 'supports', + supports, + rules: style, + }); + } + + /** + * Parse host. + */ + + function athost() { + const pos = position(); + const m = match(/^@host\s*/); + + if (!m) { + return; + } + + if (!open()) { + return error(`@host missing '{'`); + } + + const style = comments().concat(rules()); + + if (!close()) { + return error(`@host missing '}'`); + } + + return pos({ + type: 'host', + rules: style, + }); + } + + /** + * Parse media. + */ + + function atmedia() { + const pos = position(); + const m = match(/^@media *([^{]+)/); + + if (!m) { + return; + } + const media = trim(m[1]); + + if (!open()) { + return error(`@media missing '{'`); + } + + const style = comments().concat(rules()); + + if (!close()) { + return error(`@media missing '}'`); + } + + return pos({ + type: 'media', + media, + rules: style, + }); + } + + /** + * Parse custom-media. + */ + + function atcustommedia() { + const pos = position(); + const m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/); + if (!m) { + return; + } + + return pos({ + type: 'custom-media', + name: trim(m[1]), + media: trim(m[2]), + }); + } + + /** + * Parse paged media. + */ + + function atpage() { + const pos = position(); + const m = match(/^@page */); + if (!m) { + return; + } + + const sel = selector() || []; + + if (!open()) { + return error(`@page missing '{'`); + } + let decls = comments(); + + // declarations + let decl; + while ((decl = declaration())) { + decls.push(decl); + decls = decls.concat(comments()); + } + + if (!close()) { + return error(`@page missing '}'`); + } + + return pos({ + type: 'page', + selectors: sel, + declarations: decls, + }); + } + + /** + * Parse document. + */ + + function atdocument() { + const pos = position(); + const m = match(/^@([-\w]+)?document *([^{]+)/); + if (!m) { + return; + } + + const vendor = trim(m[1]); + const doc = trim(m[2]); + + if (!open()) { + return error(`@document missing '{'`); + } + + const style = comments().concat(rules()); + + if (!close()) { + return error(`@document missing '}'`); + } + + return pos({ + type: 'document', + document: doc, + vendor, + rules: style, + }); + } + + /** + * Parse font-face. + */ + + function atfontface() { + const pos = position(); + const m = match(/^@font-face\s*/); + if (!m) { + return; + } + + if (!open()) { + return error(`@font-face missing '{'`); + } + let decls = comments(); + + // declarations + let decl; + while ((decl = declaration())) { + decls.push(decl); + decls = decls.concat(comments()); + } + + if (!close()) { + return error(`@font-face missing '}'`); + } + + return pos({ + type: 'font-face', + declarations: decls, + }); + } + + /** + * Parse import + */ + + const atimport = _compileAtrule('import'); + + /** + * Parse charset + */ + + const atcharset = _compileAtrule('charset'); + + /** + * Parse namespace + */ + + const atnamespace = _compileAtrule('namespace'); + + /** + * Parse non-block at-rules + */ + + function _compileAtrule(name: string) { + const re = new RegExp( + '^@' + + name + + '\\s*((?:' + + [ + /[^\\]"(?:\\"|[^"])*"/.source, // consume any quoted parts (checking that the double quote isn't itself escaped) + /[^\\]'(?:\\'|[^'])*'/.source, // same but for single quotes + '[^;]', + ].join('|') + + ')+);', + ); + return () => { + const pos = position(); + const m = match(re); + if (!m) { + return; + } + const ret: Record = { type: name }; + ret[name] = m[1].trim(); + return pos(ret); }; - }, -}; + } + + /** + * Parse at rule. + */ + + function atrule() { + if (css[0] !== '@') { + return; + } + + return ( + atkeyframes() || + atmedia() || + atcustommedia() || + atsupports() || + atimport() || + atcharset() || + atnamespace() || + atdocument() || + atpage() || + athost() || + atfontface() + ); + } + + /** + * Parse rule. + */ + + function rule() { + const pos = position(); + const sel = selector(); + + if (!sel) { + return error('selector missing'); + } + comments(); + + return pos({ + type: 'rule', + selectors: sel, + declarations: declarations() as Declaration[], + }); + } + + return addParent(stylesheet()); +} + +/** + * Trim `str`. + */ + +function trim(str: string) { + return str ? str.replace(/^\s+|\s+$/g, '') : ''; +} + +/** + * Adds non-enumerable parent node reference to each node. + */ + +function addParent(obj: Stylesheet, parent?: Stylesheet): Stylesheet { + const isNode = obj && typeof obj.type === 'string'; + const childParent = isNode ? obj : parent; + + for (const k of Object.keys(obj)) { + const value = obj[k as keyof Stylesheet]; + if (Array.isArray(value)) { + value.forEach((v) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + addParent(v, childParent); + }); + } else if (value && typeof value === 'object') { + addParent(value as Stylesheet, childParent); + } + } + + if (isNode) { + Object.defineProperty(obj, 'parent', { + configurable: true, + writable: true, + enumerable: false, + value: parent || null, + }); + } -export { mediaSelectorPlugin, pseudoClassPlugin }; + return obj; +} diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 6641c2aa41..ccb6624c66 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -1,16 +1,13 @@ -import { mediaSelectorPlugin, pseudoClassPlugin } from './css'; +import { Rule, Media, NodeWithRules, parse } from './css'; import { - type serializedNodeWithId, - type serializedElementNodeWithId, - type serializedTextNodeWithId, + serializedNodeWithId, NodeType, - type tagMap, - type elementNode, - type BuildCache, - type legacyAttributes, + tagMap, + elementNode, + BuildCache, + legacyAttributes, } from './types'; import { isElement, Mirror, isNodeMetaEqual } from './utils'; -import postcss from 'postcss'; const tagMap: tagMap = { script: 'noscript', @@ -60,6 +57,15 @@ function getTagName(n: elementNode): string { return tagName; } +// based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping +function escapeRegExp(str: string) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +const MEDIA_SELECTOR = /(max|min)-device-(width|height)/; +const MEDIA_SELECTOR_GLOBAL = new RegExp(MEDIA_SELECTOR.source, 'g'); +const HOVER_SELECTOR = /([^\\]):hover/; +const HOVER_SELECTOR_GLOBAL = new RegExp(HOVER_SELECTOR.source, 'g'); export function adaptCssForReplay( cssText: string, cache: BuildCache, @@ -68,11 +74,70 @@ export function adaptCssForReplay( const cachedStyle = cache?.stylesWithHoverClass.get(cssText); if (cachedStyle) return cachedStyle; - const ast: { css: string } = postcss([ - mediaSelectorPlugin, - pseudoClassPlugin, - ]).process(cssText); - let result = ast.css; + const ast = parse(cssText, { + silent: true, + }); + + if (!ast.stylesheet) { + return cssText; + } + + const selectors: string[] = []; + const medias: string[] = []; + function getSelectors(rule: Rule | Media | NodeWithRules) { + if ('selectors' in rule && rule.selectors) { + rule.selectors.forEach((selector: string) => { + if (HOVER_SELECTOR.test(selector)) { + selectors.push(selector); + } + }); + } + if ('media' in rule && rule.media && MEDIA_SELECTOR.test(rule.media)) { + medias.push(rule.media); + } + if ('rules' in rule && rule.rules) { + rule.rules.forEach(getSelectors); + } + } + getSelectors(ast.stylesheet); + + let result = cssText; + if (selectors.length > 0) { + const selectorMatcher = new RegExp( + selectors + .filter((selector, index) => selectors.indexOf(selector) === index) + .sort((a, b) => b.length - a.length) + .map((selector) => { + return escapeRegExp(selector); + }) + .join('|'), + 'g', + ); + result = result.replace(selectorMatcher, (selector) => { + const newSelector = selector.replace( + HOVER_SELECTOR_GLOBAL, + '$1.\\:hover', + ); + return `${selector}, ${newSelector}`; + }); + } + if (medias.length > 0) { + const mediaMatcher = new RegExp( + medias + .filter((media, index) => medias.indexOf(media) === index) + .sort((a, b) => b.length - a.length) + .map((media) => { + return escapeRegExp(media); + }) + .join('|'), + 'g', + ); + result = result.replace(mediaMatcher, (media) => { + // not attempting to maintain min-device-width along with min-width + // (it's non standard) + return media.replace(MEDIA_SELECTOR_GLOBAL, '$1-$2'); + }); + } if (removeAnimationCss) result = result.replace(/animation.+?;/g, ''); cache?.stylesWithHoverClass.set(cssText, result); return result; @@ -85,79 +150,6 @@ export function createCache(): BuildCache { }; } -/** - * undo splitCssText/markCssSplits - * (would move to utils.ts but uses `adaptCssForReplay`) - */ -export function applyCssSplits( - n: serializedElementNodeWithId, - cssText: string, - hackCss: boolean, - cache: BuildCache, - removeAnimationCss: boolean, -): void { - const childTextNodes: serializedTextNodeWithId[] = []; - for (const scn of n.childNodes) { - if (scn.type === NodeType.Text) { - childTextNodes.push(scn); - } - } - const cssTextSplits = cssText.split('/* rr_split */'); - while ( - cssTextSplits.length > 1 && - cssTextSplits.length > childTextNodes.length - ) { - // unexpected: remerge the last two so that we don't discard any css - cssTextSplits.splice(-2, 2, cssTextSplits.slice(-2).join('')); - } - for (let i = 0; i < childTextNodes.length; i++) { - const childTextNode = childTextNodes[i]; - const cssTextSection = cssTextSplits[i]; - if (childTextNode && cssTextSection) { - // id will be assigned when these child nodes are - // iterated over in buildNodeWithSN - childTextNode.textContent = hackCss - ? adaptCssForReplay(cssTextSection, cache, removeAnimationCss) - : cssTextSection; - } - } -} - -/** - * Normally a +
hover me
" @@ -613,7 +487,7 @@ exports[`integration tests > [html file]: with-style-sheet.html 1`] = ` with style sheet - + " `; @@ -624,224 +498,11 @@ exports[`integration tests > [html file]: with-style-sheet-with-import.html 1`] with style sheet with import - - + " `; -exports[`integration tests > should be able to record elements even when .childNodes has been monkey patched 1`] = ` -"{ - \\"type\\": 0, - \\"childNodes\\": [ - { - \\"type\\": 1, - \\"name\\": \\"html\\", - \\"publicId\\": \\"\\", - \\"systemId\\": \\"\\", - \\"id\\": 2 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": { - \\"lang\\": \\"en\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 5 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"meta\\", - \\"attributes\\": { - \\"charset\\": \\"UTF-8\\" - }, - \\"childNodes\\": [], - \\"id\\": 6 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 7 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"meta\\", - \\"attributes\\": { - \\"name\\": \\"viewport\\", - \\"content\\": \\"width=device-width, initial-scale=1.0\\" - }, - \\"childNodes\\": [], - \\"id\\": 8 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 9 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"title\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"Document\\", - \\"id\\": 11 - } - ], - \\"id\\": 10 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 12 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 14 - } - ], - \\"id\\": 13 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 15 - } - ], - \\"id\\": 4 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 16 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 18 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"ul\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 20 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"li\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"a\\", - \\"id\\": 22 - } - ], - \\"id\\": 21 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 23 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"li\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"b\\", - \\"id\\": 25 - } - ], - \\"id\\": 24 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 26 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"li\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"c\\", - \\"id\\": 28 - } - ], - \\"id\\": 27 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 29 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"li\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"d\\", - \\"id\\": 31 - } - ], - \\"id\\": 30 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 32 - } - ], - \\"id\\": 19 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", - \\"id\\": 33 - } - ], - \\"id\\": 17 - } - ], - \\"id\\": 3 - } - ], - \\"id\\": 1 -}" -`; - exports[`shadow DOM integration tests > snapshot shadow DOM 1`] = ` "{ \\"type\\": 0, @@ -1094,13 +755,12 @@ exports[`shadow DOM integration tests > snapshot shadow DOM 1`] = ` { \\"type\\": 2, \\"tagName\\": \\"style\\", - \\"attributes\\": { - \\"_cssText\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px / 22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\" - }, + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\", + \\"textContent\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px / 22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\", + \\"isStyle\\": true, \\"id\\": 38 } ], diff --git a/packages/rrweb-snapshot/test/alt-css/alt-style.css b/packages/rrweb-snapshot/test/alt-css/alt-style.css deleted file mode 100644 index bda88053d8..0000000000 --- a/packages/rrweb-snapshot/test/alt-css/alt-style.css +++ /dev/null @@ -1,12 +0,0 @@ -body { - margin: 0; - background: url('../should-be-in-root-folder.jpg'); - border-image: url('data:image/svg+xml;utf8,'); - } - p { - color: red; - background: url('./should-be-in-alt-css-folder.jpg'); - } - body > p { - color: yellow; - } diff --git a/packages/rrweb-snapshot/test/css.test.ts b/packages/rrweb-snapshot/test/css.test.ts index 75e261c102..fc8e5a2946 100644 --- a/packages/rrweb-snapshot/test/css.test.ts +++ b/packages/rrweb-snapshot/test/css.test.ts @@ -1,250 +1,255 @@ -/** - * @vitest-environment jsdom - */ -import { describe, it, beforeEach, expect } from 'vitest'; -import { mediaSelectorPlugin, pseudoClassPlugin } from '../src/css'; -import postcss, { type AcceptedPlugin } from 'postcss'; -import { JSDOM } from 'jsdom'; -import { splitCssText, stringifyStylesheet } from './../src/utils'; -import { applyCssSplits } from './../src/rebuild'; -import { - NodeType, - type serializedElementNodeWithId, - type BuildCache, - type textNode, -} from '../src/types'; -import { Window } from 'happy-dom'; +import { describe, it, expect } from 'vitest'; +import { parse, Rule, Media } from '../src/css'; +import { fixSafariColons, escapeImportStatement } from './../src/utils'; describe('css parser', () => { - function parse(plugin: AcceptedPlugin, input: string): string { - const ast = postcss([plugin]).process(input, {}); - return ast.css; - } - - describe('mediaSelectorPlugin', () => { - it('selectors without device remain unchanged', () => { - const cssText = - '@media only screen and (min-width: 1200px) { .a { width: 10px; }}'; - expect(parse(mediaSelectorPlugin, cssText)).toEqual(cssText); + it('should save the filename and source', () => { + const css = 'booty {\n size: large;\n}\n'; + const ast = parse(css, { + source: 'booty.css', }); - it('can adapt media rules to replay context', () => { - [ - ['min', 'width'], - ['min', 'height'], - ['max', 'width'], - ['max', 'height'], - ].forEach(([first, second]) => { - expect( - parse( - mediaSelectorPlugin, - `@media only screen and (${first}-device-${second}: 1200px) { .a { width: 10px; }}`, - ), - ).toEqual( - `@media only screen and (${first}-${second}: 1200px) { .a { width: 10px; }}`, - ); - }); - }); + expect(ast.stylesheet!.source).toEqual('booty.css'); + + const position = ast.stylesheet!.rules[0].position!; + expect(position.start).toBeTruthy(); + expect(position.end).toBeTruthy(); + expect(position.source).toEqual('booty.css'); + expect(position.content).toEqual(css); }); - describe('pseudoClassPlugin', () => { - it('parses nested commas in selectors correctly', () => { - const cssText = - 'body > ul :is(li:not(:first-of-type) a.current, li:not(:first-of-type).active a) {background: red;}'; - expect(parse(pseudoClassPlugin, cssText)).toEqual(cssText); - }); + it('should throw when a selector is missing', () => { + expect(() => { + parse('{size: large}'); + }).toThrow(); - it("doesn't ignore :hover within :is brackets", () => { - const cssText = - 'body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a) {background: red;}'; - expect(parse(pseudoClassPlugin, cssText)) - .toEqual(`body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a), -body > ul :is(li:not(:first-of-type) a.\\:hover, li:not(:first-of-type).active a) {background: red;}`); - }); + expect(() => { + parse('b { color: red; }\n{ color: green; }\na { color: blue; }'); + }).toThrow(); + }); - it('should parse selector with comma nested inside ()', () => { - const cssText = - '[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }'; - expect(parse(pseudoClassPlugin, cssText)) - .toEqual(`[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active), -[_nghost-ng-c4172599085]:not(.fit-content).aim-select.\\:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }`); - }); + it('should throw when a broken comment is found', () => { + expect(() => { + parse('thing { color: red; } /* b { color: blue; }'); + }).toThrow(); - it('ignores ( in strings', () => { - const cssText = - 'li[attr="weirdly("] a:hover, li[attr="weirdly)"] a {background-color: red;}'; - expect(parse(pseudoClassPlugin, cssText)) - .toEqual(`li[attr="weirdly("] a:hover, li[attr="weirdly)"] a, -li[attr="weirdly("] a.\\:hover {background-color: red;}`); - }); + expect(() => { + parse('/*'); + }).toThrow(); - it('ignores escaping in strings', () => { - const cssText = `li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a {background-color: red;}`; - expect(parse(pseudoClassPlugin, cssText)) - .toEqual(`li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a, -li[attr="weirder\\"("] a.\\:hover {background-color: red;}`); - }); + /* Nested comments should be fine */ + expect(() => { + parse('/* /* */'); + }).not.toThrow(); + }); - it('ignores comma in string', () => { - const cssText = 'li[attr="has,comma"] a:hover {background: red;}'; - expect(parse(pseudoClassPlugin, cssText)).toEqual( - `li[attr="has,comma"] a:hover, -li[attr="has,comma"] a.\\:hover {background: red;}`, - ); - }); + it('should allow empty property value', () => { + expect(() => { + parse('p { color:; }'); + }).not.toThrow(); }); -}); -describe('css splitter', () => { - it('finds css textElement splits correctly', () => { - const window = new Window({ url: 'https://localhost:8080' }); - const document = window.document; - document.head.innerHTML = ''; - const style = document.querySelector('style'); - if (style) { - // as authored, e.g. no spaces - style.append('.a{background-color:black;}'); - - // how it is currently stringified (spaces present) - const expected = [ - '.a { background-color: red; }', - '.a { background-color: black; }', - ]; - const browserSheet = expected.join(''); - expect(stringifyStylesheet(style.sheet!)).toEqual(browserSheet); - - expect(splitCssText(browserSheet, style)).toEqual(expected); - } + it('should not throw with silent option', () => { + expect(() => { + parse('thing { color: red; } /* b { color: blue; }', { silent: true }); + }).not.toThrow(); }); - it('finds css textElement splits correctly when comments are present', () => { - const window = new Window({ url: 'https://localhost:8080' }); - const document = window.document; - // as authored, with comment, missing semicolons - document.head.innerHTML = - ''; - const style = document.querySelector('style'); - if (style) { - style.append('/* author comment */.a{color:red}.b{color:green}'); - - // how it is currently stringified (spaces present) - const expected = [ - '.a { color: red; } .b { color: blue; }', - '.a { color: red; } .b { color: green; }', - ]; - const browserSheet = expected.join(''); - expect(splitCssText(browserSheet, style)).toEqual(expected); - } + it('should list the parsing errors and continue parsing', () => { + const result = parse( + 'foo { color= red; } bar { color: blue; } baz {}} boo { display: none}', + { + silent: true, + source: 'foo.css', + }, + ); + + const rules = result.stylesheet!.rules; + expect(rules.length).toBeGreaterThan(2); + + const errors = result.stylesheet!.parsingErrors!; + expect(errors.length).toEqual(2); + + expect(errors[0]).toHaveProperty('message'); + expect(errors[0]).toHaveProperty('reason'); + expect(errors[0]).toHaveProperty('filename'); + expect(errors[0]).toHaveProperty('line'); + expect(errors[0]).toHaveProperty('column'); + expect(errors[0]).toHaveProperty('source'); + expect(errors[0].filename).toEqual('foo.css'); }); - it('finds css textElement splits correctly when vendor prefixed rules have been removed', () => { - const style = JSDOM.fragment(``).querySelector('style'); - if (style) { - // as authored, with newlines - style.appendChild( - JSDOM.fragment(`.x { - -webkit-transition: all 4s ease; - content: 'try to keep a newline'; - transition: all 4s ease; -}`), - ); - // TODO: splitCssText can't handle it yet if both start with .x - style.appendChild( - JSDOM.fragment(`.y { - -moz-transition: all 5s ease; - transition: all 5s ease; -}`), - ); - // browser .rules would usually omit the vendored versions and modifies the transition value - const expected = [ - '.x { content: "try to keep a newline"; background: red; transition: 4s; }', - '.y { transition: 5s; }', - ]; - const browserSheet = expected.join(''); - - // can't do this as JSDOM doesn't have style.sheet - // also happy-dom doesn't strip out vendor-prefixed rules like a real browser does - //expect(stringifyStylesheet(style.sheet!)).toEqual(browserSheet); - - expect(splitCssText(browserSheet, style)).toEqual(expected); - } + it('should parse selector with comma nested inside ()', () => { + const result = parse( + '[_nghost-ng-c4172599085]:not(.fit-content).aim-select:hover:not(:disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--disabled, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--invalid, [_nghost-ng-c4172599085]:not(.fit-content).aim-select--active) { border-color: rgb(84, 84, 84); }', + ); + + expect(result.parent).toEqual(null); + + const rules = result.stylesheet!.rules; + expect(rules.length).toEqual(1); + + let rule = rules[0] as Rule; + expect(rule.parent).toEqual(result); + expect(rule.selectors?.length).toEqual(1); + + let decl = rule.declarations![0]; + expect(decl.parent).toEqual(rule); }); -}); -describe('applyCssSplits css rejoiner', function () { - const mockLastUnusedArg = null as unknown as BuildCache; - const halfCssText = '.a { background-color: red; }'; - const otherHalfCssText = halfCssText.replace('.a', '.x'); - const markedCssText = [halfCssText, otherHalfCssText].join('/* rr_split */'); - let sn: serializedElementNodeWithId; - - beforeEach(() => { - sn = { - type: NodeType.Element, - tagName: 'style', - childNodes: [ - { - type: NodeType.Text, - textContent: '', - }, - { - type: NodeType.Text, - textContent: '', - }, - ], - } as serializedElementNodeWithId; + it('parses { and } in attribute selectors correctly', () => { + const result = parse('foo[someAttr~="{someId}"] { color: red; }'); + const rules = result.stylesheet!.rules; + + expect(rules.length).toEqual(1); + + const rule = rules[0] as Rule; + + expect(rule.selectors![0]).toEqual('foo[someAttr~="{someId}"]'); }); - it('applies css splits correctly', () => { - // happy path - applyCssSplits(sn, markedCssText, false, mockLastUnusedArg); - expect((sn.childNodes[0] as textNode).textContent).toEqual(halfCssText); - expect((sn.childNodes[1] as textNode).textContent).toEqual( - otherHalfCssText, + it('should set parent property', () => { + const result = parse( + 'thing { test: value; }\n' + + '@media (min-width: 100px) { thing { test: value; } }', ); + + expect(result.parent).toEqual(null); + + const rules = result.stylesheet!.rules; + expect(rules.length).toEqual(2); + + let rule = rules[0] as Rule; + expect(rule.parent).toEqual(result); + expect(rule.declarations!.length).toEqual(1); + + let decl = rule.declarations![0]; + expect(decl.parent).toEqual(rule); + + const media = rules[1] as Media; + expect(media.parent).toEqual(result); + expect(media.rules!.length).toEqual(1); + + rule = media.rules![0] as Rule; + expect(rule.parent).toEqual(media); + + expect(rule.declarations!.length).toEqual(1); + decl = rule.declarations![0]; + expect(decl.parent).toEqual(rule); + }); + + it('parses : in attribute selectors correctly', () => { + const out1 = fixSafariColons('[data-foo] { color: red; }'); + expect(out1).toEqual('[data-foo] { color: red; }'); + + const out2 = fixSafariColons('[data-foo:other] { color: red; }'); + expect(out2).toEqual('[data-foo\\:other] { color: red; }'); + + const out3 = fixSafariColons('[data-aa\\:other] { color: red; }'); + expect(out3).toEqual('[data-aa\\:other] { color: red; }'); }); - it('applies css splits correctly even when there are too many child nodes', () => { - let sn3 = { - type: NodeType.Element, - tagName: 'style', - childNodes: [ - { - type: NodeType.Text, - textContent: '', - }, - { - type: NodeType.Text, - textContent: '', - }, - { - type: NodeType.Text, - textContent: '', - }, - ], - } as serializedElementNodeWithId; - applyCssSplits(sn3, markedCssText, false, mockLastUnusedArg); - expect((sn3.childNodes[0] as textNode).textContent).toEqual(halfCssText); - expect((sn3.childNodes[1] as textNode).textContent).toEqual( - otherHalfCssText, + it('parses nested commas in selectors correctly', () => { + const result = parse( + ` +body > ul :is(li:not(:first-of-type) a:hover, li:not(:first-of-type).active a) { + background: red; +} +`, ); - expect((sn3.childNodes[2] as textNode).textContent).toEqual(''); + expect((result.stylesheet!.rules[0] as Rule)!.selectors!.length).toEqual(1); + + const trickresult = parse( + ` +li[attr="weirdly("] a:hover, li[attr="weirdly)"] a { + background-color: red; +} +`, + ); + expect( + (trickresult.stylesheet!.rules[0] as Rule)!.selectors!.length, + ).toEqual(2); + + const weirderresult = parse( + ` +li[attr="weirder\\"("] a:hover, li[attr="weirder\\")"] a { + background-color: red; +} +`, + ); + expect( + (weirderresult.stylesheet!.rules[0] as Rule)!.selectors!.length, + ).toEqual(2); + + const commainstrresult = parse( + ` +li[attr="has,comma"] a:hover { + background-color: red; +} +`, + ); + expect( + (commainstrresult.stylesheet!.rules[0] as Rule)!.selectors!.length, + ).toEqual(1); }); - it('maintains entire css text when there are too few child nodes', () => { - let sn1 = { - type: NodeType.Element, - tagName: 'style', - childNodes: [ - { - type: NodeType.Text, - textContent: '', - }, - ], - } as serializedElementNodeWithId; - applyCssSplits(sn1, markedCssText, false, mockLastUnusedArg); - expect((sn1.childNodes[0] as textNode).textContent).toEqual( - halfCssText + otherHalfCssText, + it('parses imports with quotes correctly', () => { + const out1 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"");`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: null, + supportsText: null, + } as unknown as CSSImportRule); + expect(out1).toEqual(`@import url("/foo.css;900;800\\"");`); + + const out2 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") supports(display: flex);`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: null, + supportsText: 'display: flex', + } as unknown as CSSImportRule); + expect(out2).toEqual( + `@import url("/foo.css;900;800\\"") supports(display: flex);`, ); + + const out3 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"");`, + href: '/foo.css;900;800"', + media: { + length: 1, + mediaText: 'print, screen', + }, + layerName: null, + supportsText: null, + } as unknown as CSSImportRule); + expect(out3).toEqual(`@import url("/foo.css;900;800\\"") print, screen;`); + + const out4 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") layer(layer-1);`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: 'layer-1', + supportsText: null, + } as unknown as CSSImportRule); + expect(out4).toEqual(`@import url("/foo.css;900;800\\"") layer(layer-1);`); + + const out5 = escapeImportStatement({ + cssText: `@import url("/foo.css;900;800"") layer;`, + href: '/foo.css;900;800"', + media: { + length: 0, + }, + layerName: '', + supportsText: null, + } as unknown as CSSImportRule); + expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`); }); }); diff --git a/packages/rrweb-snapshot/test/css/style-with-import.css b/packages/rrweb-snapshot/test/css/style-with-import.css index a24d901947..5fa59d8039 100644 --- a/packages/rrweb-snapshot/test/css/style-with-import.css +++ b/packages/rrweb-snapshot/test/css/style-with-import.css @@ -1,3 +1,2 @@ @import '//fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&family=Roboto:wght@100;300;400;500;700&display=swap"'; @import './style.css'; -@import '../alt-css/alt-style.css'; diff --git a/packages/rrweb-snapshot/test/css/style.css b/packages/rrweb-snapshot/test/css/style.css index 29b1da8ec8..2b3faf2a77 100644 --- a/packages/rrweb-snapshot/test/css/style.css +++ b/packages/rrweb-snapshot/test/css/style.css @@ -1,11 +1,11 @@ body { margin: 0; - background: url('../should-be-in-root-folder.jpg'); + background: url('../a.jpg'); border-image: url('data:image/svg+xml;utf8,'); } p { color: red; - background: url('./should-be-in-css-folder.jpg'); + background: url('./b.jpg'); } body > p { color: yellow; diff --git a/packages/rrweb-snapshot/test/html/dialog.html b/packages/rrweb-snapshot/test/html/dialog.html deleted file mode 100644 index 2380b8fade..0000000000 --- a/packages/rrweb-snapshot/test/html/dialog.html +++ /dev/null @@ -1,5 +0,0 @@ - - - I'm a dialog - - diff --git a/packages/rrweb-snapshot/test/html/monkey-patched-elements.html b/packages/rrweb-snapshot/test/html/monkey-patched-elements.html deleted file mode 100644 index a48b8fd328..0000000000 --- a/packages/rrweb-snapshot/test/html/monkey-patched-elements.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - Document - - - -
    -
  • a
  • -
  • b
  • -
  • c
  • -
  • d
  • -
- - diff --git a/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html b/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html index d98ff7b9fa..6b45f65bc5 100644 --- a/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html +++ b/packages/rrweb-snapshot/test/html/with-style-sheet-with-import.html @@ -7,10 +7,6 @@ with style sheet with import - diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index 9fa04baf65..8f4476ecb9 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -1,20 +1,10 @@ import * as fs from 'fs'; -import * as http from 'http'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import * as http from 'http'; import * as url from 'url'; -import { - afterAll, - assert, - beforeAll, - beforeEach, - describe, - expect, - it, - vi, -} from 'vitest'; - -import { getServerURL, waitForRAF } from './utils'; +import * as puppeteer from 'puppeteer'; +import { vi, assert, describe, it, beforeAll, afterAll, expect } from 'vitest'; +import { waitForRAF, getServerURL } from './utils'; const htmlFolder = path.join(__dirname, 'html'); const htmls = fs.readdirSync(htmlFolder).map((filePath) => { @@ -70,15 +60,6 @@ function sanitizeSnapshot(snapshot: string): string { return snapshot.replace(/localhost:[0-9]+/g, 'localhost:3030'); } -async function snapshot(page: puppeteer.Page, code: string): Promise { - await waitForRAF(page); - const result = (await page.evaluate(`${code} - const snapshot = rrwebSnapshot.snapshot(document); - JSON.stringify(snapshot, null, 2); - `)) as string; - return result; -} - function assertSnapshot(snapshot: string): void { expect(sanitizeSnapshot(snapshot)).toMatchSnapshot(); } @@ -87,7 +68,6 @@ interface ISuite { server: http.Server; serverURL: string; browser: puppeteer.Browser; - page: puppeteer.Page; code: string; } @@ -120,9 +100,6 @@ describe('integration tests', function (this: ISuite) { if (html.filePath.substring(html.filePath.length - 1) === '~') { continue; } - // monkey patching breaks rebuild code - if (html.filePath.includes('monkey-patched-elements.html')) continue; - const title = '[html file]: ' + html.filePath; it(title, async () => { const page: puppeteer.Page = await browser.newPage(); @@ -176,7 +153,7 @@ describe('integration tests', function (this: ISuite) { 'blob:http://localhost:xxxx/...', ); - await assertSnapshot(rebuildHtml); + assertSnapshot(rebuildHtml); }); } @@ -258,6 +235,7 @@ iframe.contentDocument.querySelector('center').clientHeight it('correctly saves cross-origin images offline', async () => { const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank', { waitUntil: 'load', }); @@ -370,7 +348,7 @@ iframe.contentDocument.querySelector('center').clientHeight it('should save background-clip: text; as the more compatible -webkit-background-clip: test;', async () => { const page: puppeteer.Page = await browser.newPage(); - await page.goto(`${serverURL}/html/background-clip-text.html`, { + await page.goto(`http://localhost:3030/html/background-clip-text.html`, { waitUntil: 'load', }); await waitForRAF(page); // wait for page to render @@ -388,10 +366,13 @@ iframe.contentDocument.querySelector('center').clientHeight it('images with inline onload should work', async () => { const page: puppeteer.Page = await browser.newPage(); - await page.goto(`${serverURL}/html/picture-with-inline-onload.html`, { - waitUntil: 'load', - }); - await page.waitForSelector('img', { timeout: 2000 }); + await page.goto( + 'http://localhost:3030/html/picture-with-inline-onload.html', + { + waitUntil: 'load', + }, + ); + await page.waitForSelector('img', { timeout: 1000 }); await page.evaluate(`${code}`); await page.evaluate(` var snapshot = rrwebSnapshot.snapshot(document, { @@ -405,22 +386,6 @@ iframe.contentDocument.querySelector('center').clientHeight )) as string; assert(fnName === 'onload'); }); - - it('should be able to record elements even when .childNodes has been monkey patched', async () => { - const page: puppeteer.Page = await browser.newPage(); - await page.goto(`${serverURL}/html/monkey-patched-elements.html`, { - waitUntil: 'load', - }); - await waitForRAF(page); // wait for page to render - const snapshotResult = JSON.stringify( - await page.evaluate(`${code}; - rrwebSnapshot.snapshot(document); - `), - null, - 2, - ); - expect(snapshotResult).toMatchSnapshot(); - }); }); describe('iframe integration tests', function (this: ISuite) { @@ -462,53 +427,6 @@ describe('iframe integration tests', function (this: ISuite) { null, 2, ); - await assertSnapshot(snapshotResult); - }); -}); - -describe('dialog integration tests', function (this: ISuite) { - vi.setConfig({ testTimeout: 30_000 }); - let server: ISuite['server']; - let serverURL: ISuite['serverURL']; - let browser: ISuite['browser']; - let code: ISuite['code']; - let page: ISuite['page']; - - beforeAll(async () => { - server = await startServer(); - serverURL = getServerURL(server); - browser = await puppeteer.launch({ - // headless: false, - }); - - code = fs.readFileSync( - path.resolve(__dirname, '../dist/rrweb-snapshot.umd.cjs'), - 'utf-8', - ); - }); - - beforeEach(async () => { - page = await browser.newPage(); - page.on('console', (msg) => console.log(msg.text())); - await page.goto(`${serverURL}/html/dialog.html`, { - waitUntil: 'load', - }); - }); - - afterAll(async () => { - await browser.close(); - await server.close(); - }); - - it('should capture open attribute for non modal dialogs', async () => { - page.evaluate('document.querySelector("dialog").show()'); - const snapshotResult = await snapshot(page, code); - assertSnapshot(snapshotResult); - }); - - it('should capture open attribute for modal dialogs', async () => { - await page.evaluate('document.querySelector("dialog").showModal()'); - const snapshotResult = await snapshot(page, code); assertSnapshot(snapshotResult); }); }); @@ -552,6 +470,6 @@ describe('shadow DOM integration tests', function (this: ISuite) { null, 2, ); - await assertSnapshot(snapshotResult); + assertSnapshot(snapshotResult); }); }); diff --git a/packages/rrweb-snapshot/test/rebuild.test.ts b/packages/rrweb-snapshot/test/rebuild.test.ts index a0994a2f88..c71e1e8510 100644 --- a/packages/rrweb-snapshot/test/rebuild.test.ts +++ b/packages/rrweb-snapshot/test/rebuild.test.ts @@ -3,34 +3,14 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import { beforeEach, describe, expect as _expect, it } from 'vitest'; +import { describe, it, beforeEach, expect } from 'vitest'; import { adaptCssForReplay, buildNodeWithSN, createCache, } from '../src/rebuild'; import { NodeType } from '../src/types'; -import { createMirror, Mirror, normalizeCssString } from '../src/utils'; - -const expect = _expect as unknown as { - (actual: T): { - toMatchCss(expected: string): void; - } & ReturnType; -} & typeof _expect; - -expect.extend({ - toMatchCss: function (received: string, expected: string) { - const pass = normalizeCssString(received) === normalizeCssString(expected); - const message: () => string = () => - pass - ? '' - : `Received (${received}) is not the same as expected (${expected})`; - return { - message, - pass, - }; - }, -}); +import { createMirror, Mirror } from '../src/utils'; function getDuration(hrtime: [number, number]) { const [seconds, nanoseconds] = hrtime; @@ -72,32 +52,6 @@ describe('rebuild', function () { }); }); - describe('rr_width/rr_height', function () { - it('rebuild blocked element with correct dimensions', function () { - const node = buildNodeWithSN( - { - id: 1, - tagName: 'svg', - type: NodeType.Element, - isSVG: true, - attributes: { - rr_width: '50px', - rr_height: '50px', - }, - childNodes: [], - }, - { - doc: document, - mirror, - hackCss: false, - cache, - }, - ) as HTMLDivElement; - expect(node.style.width).toBe('50px'); - expect(node.style.height).toBe('50px'); - }); - }); - describe('shadowDom', function () { it('rebuild shadowRoot without siblings', function () { const node = buildNodeWithSN( @@ -132,19 +86,19 @@ describe('rebuild', function () { describe('add hover class to hover selector related rules', function () { it('will do nothing to css text without :hover', () => { const cssText = 'body { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss(cssText); + expect(adaptCssForReplay(cssText, cache)).toEqual(cssText); }); it('can add hover class to css text', () => { const cssText = '.a:hover { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( '.a:hover, .a.\\:hover { color: white }', ); }); it('can correctly add hover when in middle of selector', () => { const cssText = 'ul li a:hover img { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( 'ul li a:hover img, ul li a.\\:hover img { color: white }', ); }); @@ -157,15 +111,14 @@ img, ul li.specified c:hover img { color: white }`; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( - `ul li.specified a:hover img, + expect(adaptCssForReplay(cssText, cache)).toEqual( + `ul li.specified a:hover img, ul li.specified a.\\:hover img, ul li.multiline b:hover +img, ul li.multiline +b.\\:hover img, -ul li.specified c:hover img, -ul li.specified a.\\:hover img, -ul li.multiline b.\\:hover img, -ul li.specified c.\\:hover img { +ul li.specified c:hover img, ul li.specified c.\\:hover img { color: white }`, ); @@ -173,48 +126,48 @@ ul li.specified c.\\:hover img { it('can add hover class within media query', () => { const cssText = '@media screen { .m:hover { color: white } }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( '@media screen { .m:hover, .m.\\:hover { color: white } }', ); }); it('can add hover class when there is multi selector', () => { const cssText = '.a, .b:hover, .c { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( - '.a, .b:hover, .c, .b.\\:hover { color: white }', + expect(adaptCssForReplay(cssText, cache)).toEqual( + '.a, .b:hover, .b.\\:hover, .c { color: white }', ); }); it('can add hover class when there is a multi selector with the same prefix', () => { const cssText = '.a:hover, .a:hover::after { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( - '.a:hover, .a:hover::after, .a.\\:hover, .a.\\:hover::after { color: white }', + expect(adaptCssForReplay(cssText, cache)).toEqual( + '.a:hover, .a.\\:hover, .a:hover::after, .a.\\:hover::after { color: white }', ); }); it('can add hover class when :hover is not the end of selector', () => { const cssText = 'div:hover::after { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( 'div:hover::after, div.\\:hover::after { color: white }', ); }); it('can add hover class when the selector has multi :hover', () => { const cssText = 'a:hover b:hover { color: white }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( 'a:hover b:hover, a.\\:hover b.\\:hover { color: white }', ); }); it('will ignore :hover in css value', () => { const cssText = '.a::after { content: ":hover" }'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss(cssText); + expect(adaptCssForReplay(cssText, cache)).toEqual(cssText); }); it('can adapt media rules to replay context', () => { const cssText = '@media only screen and (min-device-width : 1200px) { .a { width: 10px; }}'; - expect(adaptCssForReplay(cssText, cache)).toMatchCss( + expect(adaptCssForReplay(cssText, cache)).toEqual( '@media only screen and (min-width : 1200px) { .a { width: 10px; }}', ); }); @@ -257,7 +210,7 @@ ul li.specified c.\\:hover img { // previously that part was being incorrectly consumed by the selector regex const should_not_modify = ".tailwind :is(.before\\:content-\\[\\'\\'\\])::before { --tw-content: \":hover\"; content: var(--tw-content); }.tailwind :is(.\\[\\&\\>li\\]\\:before\\:content-\\[\\'-\\'\\] > li)::before { color: pink; }"; - expect(adaptCssForReplay(should_not_modify, cache)).toMatchCss( + expect(adaptCssForReplay(should_not_modify, cache)).toEqual( should_not_modify, ); }); @@ -266,7 +219,7 @@ ul li.specified c.\\:hover img { // the ':hover' in the below is a decoy which is not part of the selector, const should_not_modify = '@import url("https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,700;1,400&display=:hover");'; - expect(adaptCssForReplay(should_not_modify, cache)).toMatchCss( + expect(adaptCssForReplay(should_not_modify, cache)).toEqual( should_not_modify, ); }); diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index 5778eb0aff..de1d79eb6d 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -2,79 +2,64 @@ * @vitest-environment jsdom */ import { JSDOM } from 'jsdom'; -import { describe, expect, it } from 'vitest'; - -import snapshot, { - _isBlockedElement, +import { describe, it, expect } from 'vitest'; +import { + absoluteToStylesheet, serializeNodeWithId, + _isBlockedElement, } from '../src/snapshot'; -import { elementNode, serializedNodeWithId } from '../src/types'; -import { Mirror, absolutifyURLs } from '../src/utils'; - -const serializeNode = (node: Node): serializedNodeWithId | null => { - return serializeNodeWithId(node, { - doc: document, - mirror: new Mirror(), - blockClass: 'blockblock', - blockSelector: null, - maskTextClass: 'maskmask', - maskTextSelector: null, - skipChild: false, - inlineStylesheet: true, - maskTextFn: undefined, - maskInputFn: undefined, - slimDOMOptions: {}, - }); -}; +import snapshot from '../src/snapshot'; +import { serializedNodeWithId, elementNode } from '../src/types'; +import { Mirror } from '../src/utils'; describe('absolute url to stylesheet', () => { const href = 'http://localhost/css/style.css'; it('can handle relative path', () => { - expect(absolutifyURLs('url(a.jpg)', href)).toEqual( + expect(absoluteToStylesheet('url(a.jpg)', href)).toEqual( `url(http://localhost/css/a.jpg)`, ); }); it('can handle same level path', () => { - expect(absolutifyURLs('url("./a.jpg")', href)).toEqual( + expect(absoluteToStylesheet('url("./a.jpg")', href)).toEqual( `url("http://localhost/css/a.jpg")`, ); }); it('can handle parent level path', () => { - expect(absolutifyURLs('url("../a.jpg")', href)).toEqual( + expect(absoluteToStylesheet('url("../a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle absolute path', () => { - expect(absolutifyURLs('url("/a.jpg")', href)).toEqual( + expect(absoluteToStylesheet('url("/a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle external path', () => { - expect(absolutifyURLs('url("http://localhost/a.jpg")', href)).toEqual( + expect(absoluteToStylesheet('url("http://localhost/a.jpg")', href)).toEqual( `url("http://localhost/a.jpg")`, ); }); it('can handle single quote path', () => { - expect(absolutifyURLs(`url('./a.jpg')`, href)).toEqual( + expect(absoluteToStylesheet(`url('./a.jpg')`, href)).toEqual( `url('http://localhost/css/a.jpg')`, ); }); it('can handle no quote path', () => { - expect(absolutifyURLs('url(./a.jpg)', href)).toEqual( + expect(absoluteToStylesheet('url(./a.jpg)', href)).toEqual( `url(http://localhost/css/a.jpg)`, ); }); it('can handle multiple no quote paths', () => { expect( - absolutifyURLs( + absoluteToStylesheet( 'background-image: url(images/b.jpg);background: #aabbcc url(images/a.jpg) 50% 50% repeat;', href, ), @@ -85,11 +70,11 @@ describe('absolute url to stylesheet', () => { }); it('can handle data url image', () => { - expect(absolutifyURLs('url(data:image/gif;base64,ABC)', href)).toEqual( - 'url(data:image/gif;base64,ABC)', - ); expect( - absolutifyURLs( + absoluteToStylesheet('url(data:image/gif;base64,ABC)', href), + ).toEqual('url(data:image/gif;base64,ABC)'); + expect( + absoluteToStylesheet( 'url(data:application/font-woff;base64,d09GMgABAAAAAAm)', href, ), @@ -98,7 +83,7 @@ describe('absolute url to stylesheet', () => { it('preserves quotes around inline svgs with spaces', () => { expect( - absolutifyURLs( + absoluteToStylesheet( "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")", href, ), @@ -106,7 +91,7 @@ describe('absolute url to stylesheet', () => { "url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M3'/%3E%3C/svg%3E\")", ); expect( - absolutifyURLs( + absoluteToStylesheet( 'url(\'data:image/svg+xml;utf8,\')', href, ), @@ -114,7 +99,7 @@ describe('absolute url to stylesheet', () => { 'url(\'data:image/svg+xml;utf8,\')', ); expect( - absolutifyURLs( + absoluteToStylesheet( 'url("data:image/svg+xml;utf8,")', href, ), @@ -123,7 +108,7 @@ describe('absolute url to stylesheet', () => { ); }); it('can handle empty path', () => { - expect(absolutifyURLs(`url('')`, href)).toEqual(`url('')`); + expect(absoluteToStylesheet(`url('')`, href)).toEqual(`url('')`); }); }); @@ -154,6 +139,22 @@ describe('isBlockedElement()', () => { }); describe('style elements', () => { + const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + mirror: new Mirror(), + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + }); + }; + const render = (html: string): HTMLStyleElement => { document.write(html); return document.querySelector('style')!; @@ -162,32 +163,44 @@ describe('style elements', () => { it('should serialize all rules of stylesheet when the sheet has a single child node', () => { const styleEl = render(``); styleEl.sheet?.insertRule('section { color: blue; }'); - expect(serializeNode(styleEl)).toMatchObject({ + expect(serializeNode(styleEl.childNodes[0])).toMatchObject({ + isStyle: true, rootId: undefined, - attributes: { - _cssText: 'section {color: blue;}body {color: red;}', - }, - type: 2, + textContent: 'section {color: blue;}body {color: red;}', + type: 3, }); }); - it('should serialize all rules on stylesheets with mix of insertion type', () => { + it('should serialize individual text nodes on stylesheets with multiple child nodes', () => { const styleEl = render(``); - styleEl.sheet?.insertRule('section.lost { color: unseeable; }'); // browser throws this away after append styleEl.append(document.createTextNode('section { color: blue; }')); - styleEl.sheet?.insertRule('section.working { color: pink; }'); - expect(serializeNode(styleEl)).toMatchObject({ + expect(serializeNode(styleEl.childNodes[1])).toMatchObject({ + isStyle: true, rootId: undefined, - attributes: { - _cssText: - 'section.working {color: pink;}body {color: red;}/* rr_split */section {color: blue;}', - }, - type: 2, + textContent: 'section { color: blue; }', + type: 3, }); }); }); describe('scrollTop/scrollLeft', () => { + const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + mirror: new Mirror(), + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + newlyAddedElement: false, + }); + }; + const render = (html: string): HTMLDivElement => { document.write(html); return document.querySelector('div')!; @@ -209,6 +222,23 @@ describe('scrollTop/scrollLeft', () => { }); describe('form', () => { + const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + mirror: new Mirror(), + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + newlyAddedElement: false, + }); + }; + const render = (html: string): HTMLTextAreaElement => { document.write(html); return document.querySelector('textarea')!; diff --git a/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts b/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts deleted file mode 100644 index 1e42bab1a6..0000000000 --- a/packages/rrweb-snapshot/test/stringify-stylesheet.bench.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @vitest-environment jsdom - */ -import { bench } from 'vitest'; -import * as fs from 'fs'; -import * as path from 'path'; -import { stringifyStylesheet } from '../src/utils'; -import * as CSSOM from 'cssom'; - -describe('stringifyStylesheet', () => { - let benchmarkStylesheet: CSSStyleSheet; - - const cssText = fs.readFileSync( - path.resolve(__dirname, './css/benchmark.css'), - 'utf8', - ); - benchmarkStylesheet = CSSOM.parse(cssText); - benchmarkStylesheet.href = 'https://example.com/style.css'; - - it.skip('stringify', () => { - // written just to ensure it's working - const cssText = '.x { background: url(./relative.jpg) }'; - const styleSheet = CSSOM.parse(cssText); - styleSheet.href = 'https://example.com/style.css'; - expect(stringifyStylesheet(styleSheet)).toEqual( - 'x {background: url(https://example.com/relative.jpg);}', - ); - }); - - bench( - 'stringify', - () => { - stringifyStylesheet(benchmarkStylesheet); - }, - { time: 1000 }, - ); -}); diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts index 885f4ec385..2b09602ae4 100644 --- a/packages/rrweb-snapshot/test/utils.test.ts +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -3,12 +3,7 @@ */ import { describe, it, test, expect } from 'vitest'; import { NodeType, serializedNode } from '../src/types'; -import { - escapeImportStatement, - extractFileExtension, - fixSafariColons, - isNodeMetaEqual, -} from '../src/utils'; +import { extractFileExtension, isNodeMetaEqual } from '../src/utils'; import type { serializedNodeWithId } from '@saola.ai/rrweb-snapshot'; describe('utils', () => { @@ -204,80 +199,4 @@ describe('utils', () => { expect(extension).toBe('js'); }); }); - - describe('escapeImportStatement', () => { - it('parses imports with quotes correctly', () => { - const out1 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"");`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: null, - supportsText: null, - } as unknown as CSSImportRule); - expect(out1).toEqual(`@import url("/foo.css;900;800\\"");`); - - const out2 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") supports(display: flex);`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: null, - supportsText: 'display: flex', - } as unknown as CSSImportRule); - expect(out2).toEqual( - `@import url("/foo.css;900;800\\"") supports(display: flex);`, - ); - - const out3 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"");`, - href: '/foo.css;900;800"', - media: { - length: 1, - mediaText: 'print, screen', - }, - layerName: null, - supportsText: null, - } as unknown as CSSImportRule); - expect(out3).toEqual(`@import url("/foo.css;900;800\\"") print, screen;`); - - const out4 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") layer(layer-1);`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: 'layer-1', - supportsText: null, - } as unknown as CSSImportRule); - expect(out4).toEqual( - `@import url("/foo.css;900;800\\"") layer(layer-1);`, - ); - - const out5 = escapeImportStatement({ - cssText: `@import url("/foo.css;900;800"") layer;`, - href: '/foo.css;900;800"', - media: { - length: 0, - }, - layerName: '', - supportsText: null, - } as unknown as CSSImportRule); - expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`); - }); - }); - describe('fixSafariColons', () => { - it('parses : in attribute selectors correctly', () => { - const out1 = fixSafariColons('[data-foo] { color: red; }'); - expect(out1).toEqual('[data-foo] { color: red; }'); - - const out2 = fixSafariColons('[data-foo:other] { color: red; }'); - expect(out2).toEqual('[data-foo\\:other] { color: red; }'); - - const out3 = fixSafariColons('[data-aa\\:other] { color: red; }'); - expect(out3).toEqual('[data-aa\\:other] { color: red; }'); - }); - }); }); diff --git a/packages/rrweb-snapshot/tsconfig.json b/packages/rrweb-snapshot/tsconfig.json index cd2eb36538..82d5cc086b 100644 --- a/packages/rrweb-snapshot/tsconfig.json +++ b/packages/rrweb-snapshot/tsconfig.json @@ -1,13 +1,7 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], - "exclude": [ - "vite.config.ts", - "vitest.config.ts", - "test" - ], + "include": ["src"], + "exclude": ["vite.config.ts", "vitest.config.ts", "test"], "compilerOptions": { "rootDir": "src", "tsBuildInfoFile": "./tsconfig.tsbuildinfo" diff --git a/packages/rrweb-snapshot/vitest.config.ts b/packages/rrweb-snapshot/vitest.config.ts index 1b5a8b7e3e..39888437cf 100644 --- a/packages/rrweb-snapshot/vitest.config.ts +++ b/packages/rrweb-snapshot/vitest.config.ts @@ -6,7 +6,7 @@ export default mergeConfig( configShared, defineProject({ test: { - globals: true, + // ... custom test config here }, }), ); diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index 69641c0e3d..6820fed9d8 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,27 @@ # rrweb +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.15 + - @saola.ai/rrdom@2.0.15 + - @saola.ai/rrweb-types@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.14 + - @saola.ai/rrdom@2.0.14 + - @saola.ai/rrweb-types@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 3345ec6f26..643fad75b8 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,25 +1,24 @@ { "name": "@saola.ai/rrweb", - "version": "2.0.13", + "version": "2.0.15", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", "prepack": "npm run build", - "retest": "cross-env PUPPETEER_HEADLESS=true yarn retest:headful", - "retest:headful": "vitest run --exclude test/benchmark", + "retest": "vitest run --exclude test/benchmark", "build-and-test": "yarn build && yarn retest", - "test:headless": "cross-env PUPPETEER_HEADLESS=true yarn build-and-test", - "test:headful": "cross-env PUPPETEER_HEADLESS=false yarn build-and-test", + "test:headless": "PUPPETEER_HEADLESS=true yarn build-and-test", + "test:headful": "PUPPETEER_HEADLESS=false yarn build-and-test", "test": "yarn test:headless", - "test:watch": "yarn build && cross-env PUPPETEER_HEADLESS=true yarn vitest --exclude test/benchmark", + "test:watch": "yarn build && PUPPETEER_HEADLESS=true yarn vitest --exclude test/benchmark", "test:update": "yarn test:headless --update", - "retest:update": "cross-env PUPPETEER_HEADLESS=true yarn retest --update", + "retest:update": "PUPPETEER_HEADLESS=true yarn retest --update", "repl": "yarn build && node scripts/repl.js", "live-stream": "yarn build && node scripts/stream.js", "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src", "benchmark": "vitest run --maxConcurrency 1 --no-file-parallelism test/benchmark" }, @@ -69,6 +68,7 @@ "@types/node": "^18.15.11", "@types/offscreencanvas": "^2019.6.4", "construct-style-sheets-polyfill": "^3.1.0", + "cross-env": "^5.2.0", "fast-mhtml": "^1.1.9", "identity-obj-proxy": "^3.0.0", "ignore-styles": "^5.0.1", @@ -78,18 +78,17 @@ "simple-peer-light": "^9.10.0", "ts-node": "^10.9.1", "tslib": "^2.3.1", - "typescript": "^5.4.5", - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "typescript": "^4.7.3", + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.13", - "@saola.ai/rrweb-utils": "^2.0.13", + "@saola.ai/rrweb-types": "^2.0.15", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", - "@saola.ai/rrdom": "^2.0.13", - "@saola.ai/rrweb-snapshot": "^2.0.13" + "@saola.ai/rrdom": "^2.0.15", + "@saola.ai/rrweb-snapshot": "^2.0.15" } } diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index cbb8c566d9..e013aab4e7 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -17,24 +17,19 @@ export { type eventWithTime, } from '@saola.ai/rrweb-types'; -// exports style.css from replay -import './replay/styles/style.css'; - export type { recordOptions, ReplayPlugin } from './types'; const { addCustomEvent } = record; const { freezePage } = record; -const { takeFullSnapshot } = record; export { record, addCustomEvent, freezePage, - takeFullSnapshot, Replayer, - type playerConfig, - type PlayerMachineState, - type SpeedMachineState, + playerConfig, + PlayerMachineState, + SpeedMachineState, canvasMutation, _mirror as mirror, utils, diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index a5bdd977b8..7320a3c61e 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -1,7 +1,7 @@ import { snapshot, - type MaskInputOptions, - type SlimDOMOptions, + MaskInputOptions, + SlimDOMOptions, createMirror, } from '@saola.ai/rrweb-snapshot'; import { initObservers, mutationBuffers } from './observer'; @@ -19,14 +19,14 @@ import { import type { recordOptions } from '../types'; import { EventType, - type eventWithoutTime, - type eventWithTime, + eventWithoutTime, + eventWithTime, IncrementalSource, - type listenerHandler, - type mutationCallbackParam, - type scrollCallback, - type canvasMutationParam, - type adoptedStyleSheetParam, + listenerHandler, + mutationCallbackParam, + scrollCallback, + canvasMutationParam, + adoptedStyleSheetParam, } from '@saola.ai/rrweb-types'; import type { CrossOriginIframeMessageEventContent } from '../types'; import { IframeManager } from './iframe-manager'; @@ -39,7 +39,6 @@ import { registerErrorHandler, unregisterErrorHandler, } from './error-handler'; -import dom from '@rrweb/utils'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; @@ -384,7 +383,6 @@ function record( inlineStylesheet, maskAllInputs: maskInputOptions, maskTextFn, - maskInputFn, slimDOM: slimDOMOptions, dataURLOptions, recordCanvas, @@ -397,8 +395,7 @@ function record( stylesheetManager.trackLinkElement(n as HTMLLinkElement); } if (hasShadowRoot(n)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - shadowDomManager.addShadowRoot(dom.shadowRoot(n as Node)!, document); + shadowDomManager.addShadowRoot(n.shadowRoot, document); } }, onIframeLoad: (iframe, childSn) => { diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 53537d03d2..23ad493654 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -32,7 +32,6 @@ import { getShadowHost, closestElementOfNode, } from '../utils'; -import dom from '@rrweb/utils'; type DoubleLinkedListNode = { previous: DoubleLinkedListNode | null; @@ -169,7 +168,6 @@ export default class MutationBuffer { private addedSet = new Set(); private movedSet = new Set(); private droppedSet = new Set(); - private removesSubTreeCache = new Set(); private mutationCb: observerParam['mutationCb']; private blockClass: observerParam['blockClass']; @@ -287,27 +285,16 @@ export default class MutationBuffer { return nextId; }; const pushAdd = (n: Node) => { - const parent = dom.parentNode(n); - if (!parent || !inDom(n)) { + if ( + !n.parentNode || + !inDom(n) || + (n.parentNode as Element).tagName === 'TEXTAREA' + ) { return; } - let cssCaptured = false; - if (n.nodeType === Node.TEXT_NODE) { - const parentTag = (parent as Element).tagName; - if (parentTag === 'TEXTAREA') { - // genTextAreaValueMutation already called via parent - return; - } else if (parentTag === 'STYLE' && this.addedSet.has(parent)) { - // css content will be recorded via parent's _cssText attribute when - // mutation adds entire - - - - - - - - - diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 0ae70af289..34d81da850 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -81,7 +81,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can record form interactions', async () => { @@ -98,10 +98,10 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); - it('can record and replay textarea mutations correctly', async () => { + it('can record textarea mutations correctly', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'empty.html')); @@ -112,29 +112,20 @@ describe('record integration tests', function (this: ISuite) { const ta = document.createElement('textarea'); ta.innerText = 'pre value'; document.body.append(ta); - - const ta2 = document.createElement('textarea'); - ta2.id = 'ta2'; - document.body.append(ta2); }); - await waitForRAF(page); + await page.waitForTimeout(5); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; t.innerText = 'ok'; // this mutation should be recorded - - const ta2t = document.createTextNode('added'); - document.getElementById('ta2').append(ta2t); }); - await waitForRAF(page); + await page.waitForTimeout(5); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; (t.childNodes[0] as Text).appendData('3'); // this mutation is also valid - - document.getElementById('ta2').remove(); // done with this }); - await waitForRAF(page); + await page.waitForTimeout(5); await page.type('textarea', '1'); // types (inserts) at index 0, in front of existing text - await waitForRAF(page); + await page.waitForTimeout(5); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; // user has typed so childNode content should now be ignored @@ -145,13 +136,13 @@ describe('record integration tests', function (this: ISuite) { // there is nothing explicit in rrweb which enforces this, but this test may protect against // a future change where a mutation on a textarea incorrectly updates the .value }); - await waitForRAF(page); + await page.waitForTimeout(5); await page.type('textarea', '2'); // cursor is at index 1 const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); // check after each mutation and text input const replayTextareaValues = await page.evaluate(` @@ -162,18 +153,12 @@ describe('record integration tests', function (this: ISuite) { replayer.pause((e.timestamp - window.snapshots[0].timestamp)+1); let ts = replayer.iframe.contentDocument.querySelector('textarea'); vals.push((e.data.source === 0 ? 'Mutation' : 'User') + ':' + ts.value); - let ts2 = replayer.iframe.contentDocument.getElementById('ta2'); - if (ts2) { - vals.push('ta2:' + ts2.value); - } }); vals; `); expect(replayTextareaValues).toEqual([ 'Mutation:pre value', - 'ta2:', 'Mutation:ok', - 'ta2:added', 'Mutation:ok3', 'User:1ok3', 'Mutation:1ok3', // if this gets set to 'ignore', it's an error, as the 'user' has modified the textarea @@ -181,131 +166,6 @@ describe('record integration tests', function (this: ISuite) { ]); }); - it('can record and replay style mutations', async () => { - // This test shows that the `isStyle` attribute on textContent is not needed in a mutation - // TODO: we could get a lot more elaborate here with mixed textContent and insertRule mutations - const page: puppeteer.Page = await browser.newPage(); - await page.goto(`${serverURL}/html`); - await page.setContent(getHtml.call(this, 'style.html')); - - await waitForRAF(page); // ensure mutations aren't included in fullsnapshot - - await page.evaluate(() => { - let styleEl = document.querySelector('style#dual-textContent'); - if (styleEl) { - styleEl.append( - document.createTextNode('body { background-color: darkgreen; }'), - ); - styleEl.append( - document.createTextNode( - '.absolutify { background-image: url("./rel"); }', - ), - ); - } - }); - await waitForRAF(page); - await page.evaluate(() => { - let styleEl = document.querySelector('style#dual-textContent'); - if (styleEl) { - styleEl.childNodes.forEach((cn) => { - if (cn.textContent) { - cn.textContent = cn.textContent.replace('darkgreen', 'purple'); - cn.textContent = cn.textContent.replace( - 'orange !important', - 'yellow', - ); - } - }); - } - }); - await waitForRAF(page); - await page.evaluate(() => { - let styleEl = document.querySelector('style#dual-textContent'); - if (styleEl) { - styleEl.childNodes.forEach((cn) => { - if (cn.textContent) { - cn.textContent = cn.textContent.replace( - 'black', - 'black !important', - ); - } - }); - } - let hoverMutationStyleEl = document.querySelector('style#hover-mutation'); - if (hoverMutationStyleEl) { - hoverMutationStyleEl.childNodes.forEach((cn) => { - if (cn.textContent) { - cn.textContent = 'a:hover { outline: cyan solid 1px; }'; - } - }); - } - let st = document.createElement('style'); - st.id = 'goldilocks'; - st.innerText = 'body { color: brown }'; - document.body.append(st); - }); - - await waitForRAF(page); - await page.evaluate(() => { - let styleEl = document.querySelector('style#goldilocks'); - if (styleEl) { - styleEl.childNodes.forEach((cn) => { - if (cn.textContent) { - cn.textContent = cn.textContent.replace('brown', 'gold'); - } - }); - } - }); - - const snapshots = (await page.evaluate( - 'window.snapshots', - )) as eventWithTime[]; - - // following ensures that the ./rel url has been absolutized (in a mutation) - await assertSnapshot(snapshots); - - // check after each mutation and text input - const replayStyleValues = await page.evaluate(` - const { Replayer } = rrweb; - const replayer = new Replayer(window.snapshots); - const vals = []; - window.snapshots.filter((e)=>e.data.attributes || e.data.source === 5).forEach((e)=>{ - replayer.pause((e.timestamp - window.snapshots[0].timestamp)+1); - let bodyStyle = getComputedStyle(replayer.iframe.contentDocument.querySelector('body')) - vals.push({ - 'background-color': bodyStyle['background-color'], - 'color': bodyStyle['color'], - }); - }); - vals.push(replayer.iframe.contentDocument.getElementById('single-textContent').innerText); - vals.push(replayer.iframe.contentDocument.getElementById('empty').innerText); - vals.push(replayer.iframe.contentDocument.getElementById('hover-mutation').innerText); - vals; -`); - - expect(replayStyleValues).toEqual([ - { - 'background-color': 'rgb(0, 100, 0)', // darkgreen - color: 'rgb(255, 165, 0)', // orange (from style.html) - }, - { - 'background-color': 'rgb(128, 0, 128)', // purple - color: 'rgb(255, 255, 0)', // yellow - }, - { - 'background-color': 'rgb(0, 0, 0)', // black !important - color: 'rgb(165, 42, 42)', // brown - }, - { - 'background-color': 'rgb(0, 0, 0)', - color: 'rgb(255, 215, 0)', // gold - }, - 'a:hover,\na.\\:hover { outline: red solid 1px; }', // has run adaptCssForReplay - 'a:hover,\na.\\:hover { outline: blue solid 1px; }', // has run adaptCssForReplay - 'a:hover,\na.\\:hover { outline: cyan solid 1px; }', // has run adaptCssForReplay after text mutation - ]); - }); - it('can record childList mutations', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); @@ -323,7 +183,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can record character data muatations', async () => { @@ -345,7 +205,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can record attribute mutation', async () => { @@ -365,7 +225,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('handles null attribute values', async () => { @@ -393,7 +253,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can record node mutations', async () => { @@ -412,7 +272,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can record style changes compactly and preserve css var() functions', async () => { @@ -462,7 +322,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can freeze mutations', async () => { @@ -498,7 +358,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should not record input events on ignored elements', async () => { @@ -534,7 +394,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can use maskInputOptions to configure which type of inputs should be masked', async () => { @@ -560,7 +420,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should mask password value attribute with maskInputOptions', async () => { @@ -584,7 +444,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should mask inputs via function call', async () => { @@ -614,7 +474,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record input userTriggered values if userTriggeredOnInput is enabled', async () => { @@ -634,7 +494,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should not record blocked elements and its child nodes', async () => { @@ -649,7 +509,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should not record blocked elements dynamically added', async () => { @@ -671,7 +531,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('mutations should work when blocked class is unblocked', async () => { @@ -692,7 +552,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record DOM node movement 1', async () => { @@ -712,7 +572,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record DOM node movement 2', async () => { @@ -729,7 +589,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record dynamic CSS changes', async () => { @@ -740,7 +600,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record canvas mutations', async () => { @@ -765,7 +625,7 @@ describe('record integration tests', function (this: ISuite) { }); } } - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should not record input values if dynamically added and maskAllInputs is true', async () => { @@ -802,7 +662,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can correctly serialize a shader and multiple webgl contexts', async () => { @@ -817,7 +677,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('will serialize node before record', async () => { @@ -838,7 +698,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('will defer missing next node mutation', async () => { @@ -875,7 +735,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record images with blob url', async () => { @@ -892,7 +752,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record images inside iframe with blob url', async () => { @@ -909,7 +769,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record images inside iframe with blob url after iframe was reloaded', async () => { @@ -932,7 +792,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record shadow DOM', async () => { @@ -982,7 +842,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record shadow DOM 2', async () => { @@ -1007,7 +867,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record shadow DOM 3', async () => { @@ -1028,7 +888,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record moved shadow DOM', async () => { @@ -1057,7 +917,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record moved shadow DOM 2', async () => { @@ -1095,7 +955,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record nested iframes and shadow doms', async () => { @@ -1140,7 +1000,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record mutations in iframes accross pages', async () => { @@ -1170,7 +1030,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); // https://github.com/webcomponents/polyfills/tree/master/packages/shadydom @@ -1204,7 +1064,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); // https://github.com/salesforce/lwc/tree/master/packages/%40lwc/synthetic-shadow @@ -1246,7 +1106,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should mask texts', async () => { @@ -1261,7 +1121,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should mask texts using maskTextFn', async () => { @@ -1277,7 +1137,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should unmask texts using maskTextFn', async () => { @@ -1297,7 +1157,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can mask character data mutations', async () => { @@ -1326,7 +1186,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('can mask character data mutations with regexp', async () => { @@ -1359,7 +1219,7 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record after DOMContentLoaded event', async () => { @@ -1374,175 +1234,6 @@ describe('record integration tests', function (this: ISuite) { const snapshots = (await page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); - }); - - /** - * the regression part of the following is now handled by replayer.test.ts::'can deal with duplicate/conflicting values on style elements' - * so this test could be dropped if we add more robust mixing of `insertRule` into 'can record and replay style mutations' - */ - it('should record style mutations and replay them correctly', async () => { - const page: puppeteer.Page = await browser.newPage(); - const OldColor = 'rgb(255, 0, 0)'; // red color - const NewColor = 'rgb(255, 255, 0)'; // yellow color - - await page.setContent( - ` - - - - - -
-
- - - `, - ); - // Start rrweb recording - await page.evaluate( - (code, recordSnippet) => { - const script = document.createElement('script'); - script.textContent = `${code}window.Date.now = () => new Date(Date.UTC(2018, 10, 15, 8)).valueOf();${recordSnippet}`; - document.head.appendChild(script); - }, - code, - generateRecordSnippet({}), - ); - - await page.evaluate( - async (OldColor, NewColor) => { - // Create a new style element with the same content as the existing style element and apply it to the #two div element - const incrementalStyle = document.createElement( - 'style', - ) as HTMLStyleElement; - incrementalStyle.textContent = ` \n`; - document.head.appendChild(incrementalStyle); - incrementalStyle.sheet!.insertRule(`#two { color: ${OldColor}; }`, 0); - - await new Promise((resolve) => - requestAnimationFrame(() => { - requestAnimationFrame(resolve); - }), - ); - - // Change the color of the #one div element to yellow as an incremental style mutation - const styleElement = document.querySelector('style')!; - (styleElement.sheet!.cssRules[0] as any).style.setProperty( - 'color', - NewColor, - ); - // Change the color of the #two div element to yellow as an incremental style mutation - (incrementalStyle.sheet!.cssRules[0] as any).style.setProperty( - 'color', - NewColor, - ); - }, - OldColor, - NewColor, - ); - await waitForRAF(page); - - const snapshots = (await page.evaluate( - 'window.snapshots', - )) as eventWithTime[]; - await assertSnapshot(snapshots); - - /** - * Replay the recorded events and check if the style mutation is applied correctly - */ - const changedColors = await page.evaluate(` - const { Replayer } = rrweb; - const replayer = new Replayer(window.snapshots); - replayer.pause(1000); - - // Get the color of the element after applying the style mutation event - [ - window.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#one'), - ).color, - window.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#two'), - ).color, - ]; - `); - expect(changedColors).toEqual([NewColor, NewColor]); - await page.close(); - }); - - it('should record style mutations with multiple child nodes and replay them correctly', async () => { - // ensure that presence of multiple text nodes doesn't interfere with programmatic insertRule operations - - const page: puppeteer.Page = await browser.newPage(); - const Color = 'rgb(255, 0, 0)'; // red color - - await page.setContent( - ` - - - - - -
-
- - - `, - ); - // Start rrweb recording - await page.evaluate( - (code, recordSnippet) => { - const script = document.createElement('script'); - script.textContent = `${code};${recordSnippet}`; - document.head.appendChild(script); - }, - code, - generateRecordSnippet({}), - ); - - await page.evaluate(async (Color) => { - // Create a new style element with the same content as the existing style element and apply it to the #two div element - const incrementalStyle = document.createElement( - 'style', - ) as HTMLStyleElement; - incrementalStyle.append(document.createTextNode('/* hello */')); - incrementalStyle.append(document.createTextNode('/* world */')); - document.head.appendChild(incrementalStyle); - incrementalStyle.sheet!.insertRule(`#two { color: ${Color}; }`, 0); - }, Color); - - const snapshots = (await page.evaluate( - 'window.snapshots', - )) as eventWithTime[]; - await assertSnapshot(snapshots); - - /** - * Replay the recorded events and check if the style mutation is applied correctly - */ - const changedColors = await page.evaluate(` - const { Replayer } = rrweb; - const replayer = new Replayer(window.snapshots); - replayer.pause(1000); - - // Get the color of the element after applying the style mutation event - [ - window.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#one'), - ).color, - window.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#two'), - ).color, - ]; - `); - expect(changedColors).toEqual([Color, Color]); - await page.close(); + assertSnapshot(snapshots); }); }); diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 72a8a7fbe8..d35dd6f6a2 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -206,7 +206,7 @@ describe('record', function (this: ISuite) { }, 10); }); await ctx.page.waitForTimeout(100); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('should record scroll position', async () => { @@ -223,7 +223,7 @@ describe('record', function (this: ISuite) { p.scrollLeft = 10; }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('should record selection event', async () => { @@ -279,7 +279,7 @@ describe('record', function (this: ISuite) { }); }); await ctx.page.waitForTimeout(50); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures stylesheet rules', async () => { @@ -297,7 +297,6 @@ describe('record', function (this: ISuite) { // begin: pre-serialization const ruleIdx0 = styleSheet.insertRule('body { background: #000; }'); const ruleIdx1 = styleSheet.insertRule('body { background: #111; }'); - styleSheet.deleteRule(ruleIdx1); // end: pre-serialization setTimeout(() => { @@ -329,71 +328,8 @@ describe('record', function (this: ISuite) { rule: 'body { color: #fff; }', }, ]); - expect((addRules[1].data as styleSheetRuleData).adds).toEqual([ - { - rule: 'body { color: #ccc; }', - }, - ]); - expect(removeRuleCount).toEqual(1); - await assertSnapshot(ctx.events); - }); - - it('captures stylesheet rules with deprecated addRule & removeRule properties', async () => { - await ctx.page.evaluate(() => { - const { record } = (window as unknown as IWindow).rrweb; - - record({ - emit: (window as unknown as IWindow).emit, - }); - - const styleElement = document.createElement('style'); - document.head.appendChild(styleElement); - - const styleSheet = styleElement.sheet; - // begin: pre-serialization - const ruleIdx0 = styleSheet.addRule('body', 'background: #000;'); - const ruleIdx1 = styleSheet.addRule('body', 'background: #111;'); - - styleSheet.removeRule(ruleIdx1); - // end: pre-serialization - setTimeout(() => { - styleSheet.addRule('body', 'color: #fff;'); - }, 0); - setTimeout(() => { - styleSheet.removeRule(ruleIdx0); - }, 5); - setTimeout(() => { - styleSheet.addRule('body', 'color: #ccc;'); - }, 10); - }); - await ctx.page.waitForTimeout(50); - const styleSheetRuleEvents = ctx.events.filter( - (e) => - e.type === EventType.IncrementalSnapshot && - e.data.source === IncrementalSource.StyleSheetRule, - ); - const addRules = styleSheetRuleEvents.filter((e) => - Boolean((e.data as styleSheetRuleData).adds), - ); - const removeRuleCount = styleSheetRuleEvents.filter((e) => - Boolean((e.data as styleSheetRuleData).removes), - ).length; - // pre-serialization insert/delete should be ignored - expect(addRules.length).toEqual(2); - expect((addRules[0].data as styleSheetRuleData).adds).toEqual([ - { - index: 1, - rule: 'body { color: #fff; }', - }, - ]); - expect((addRules[1].data as styleSheetRuleData).adds).toEqual([ - { - index: 1, - rule: 'body { color: #ccc; }', - }, - ]); expect(removeRuleCount).toEqual(1); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); const captureNestedStylesheetRulesTest = async () => { @@ -439,7 +375,7 @@ describe('record', function (this: ISuite) { // sync insert/delete should be ignored expect(addRuleCount).toEqual(2); expect(removeRuleCount).toEqual(1); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }; it('captures nested stylesheet rules', captureNestedStylesheetRulesTest); @@ -490,7 +426,7 @@ describe('record', function (this: ISuite) { }, 0); }); await ctx.page.waitForTimeout(50); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures inserted style text nodes correctly', async () => { @@ -510,7 +446,7 @@ describe('record', function (this: ISuite) { styleEl.append(document.createTextNode('h1 { color: pink; }')); }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures stylesheets with `blob:` url', async () => { @@ -537,7 +473,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures mutations on adopted stylesheets', async () => { @@ -602,7 +538,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures adopted stylesheets in nested shadow doms and iframes', async () => { @@ -654,7 +590,7 @@ describe('record', function (this: ISuite) { }, 150); }); await ctx.page.waitForTimeout(200); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures adopted stylesheets of shadow doms in checkout full snapshot', async () => { @@ -683,7 +619,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures stylesheets in iframes with `blob:` url', async () => { @@ -715,7 +651,7 @@ describe('record', function (this: ISuite) { }); }); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('aggregates mutations', async () => { @@ -762,7 +698,7 @@ describe('record', function (this: ISuite) { ); expect(mutationEvents.length).toEqual(0); // there was no aggregate effect - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('no need for attribute mutations on adds', async () => { @@ -801,7 +737,7 @@ describe('record', function (this: ISuite) { ); expect(mutationEvents.length).toEqual(1); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); describe('loading stylesheets', () => { @@ -853,7 +789,7 @@ describe('record', function (this: ISuite) { await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`); await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures stylesheets in iframes that are still loading', async () => { @@ -887,7 +823,7 @@ describe('record', function (this: ISuite) { await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); }); @@ -913,7 +849,7 @@ describe('record', function (this: ISuite) { await ctx.page.waitForResponse(corsStylesheetURL); // wait for stylesheet to be loaded await waitForRAF(ctx.page); // wait for rrweb to emit events - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('captures adopted stylesheets in shadow doms and iframe', async () => { @@ -988,7 +924,7 @@ describe('record', function (this: ISuite) { }); await waitForRAF(ctx.page); // wait till events get sent - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); }); @@ -1089,6 +1025,6 @@ describe('record iframes', function (this: ISuite) { expect(styleRelatedEvents.length).toEqual(5); expect(addRuleCount).toEqual(2); expect(removeRuleCount).toEqual(2); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); }); diff --git a/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap b/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap deleted file mode 100644 index 03526f8c0f..0000000000 --- a/packages/rrweb/test/record/__snapshots__/dialog.test.ts.snap +++ /dev/null @@ -1,487 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`dialog > add dialog and show 1`] = ` -"[ - { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1920, - \\"height\\": 1080 - } - }, - { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": { - \\"type\\": \\"text/javascript\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 5 - } - ], - \\"id\\": 4 - } - ], - \\"id\\": 3 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 7 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"I'm a dialog\\", - \\"id\\": 9 - } - ], - \\"id\\": 8 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", - \\"id\\": 10 - } - ], - \\"id\\": 6 - } - ], - \\"id\\": 2 - } - ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [], - \\"adds\\": [ - { - \\"parentId\\": 6, - \\"nextId\\": null, - \\"node\\": { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"non-modal\\" - }, - \\"childNodes\\": [], - \\"id\\": 11 - } - } - ] - } - } -]" -`; - -exports[`dialog > add dialog and showModal 1`] = ` -"[ - { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1920, - \\"height\\": 1080 - } - }, - { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": { - \\"type\\": \\"text/javascript\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 5 - } - ], - \\"id\\": 4 - } - ], - \\"id\\": 3 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 7 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"I'm a dialog\\", - \\"id\\": 9 - } - ], - \\"id\\": 8 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", - \\"id\\": 10 - } - ], - \\"id\\": 6 - } - ], - \\"id\\": 2 - } - ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [], - \\"adds\\": [ - { - \\"parentId\\": 6, - \\"nextId\\": null, - \\"node\\": { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"modal\\" - }, - \\"childNodes\\": [], - \\"id\\": 11 - } - } - ] - } - } -]" -`; - -exports[`dialog > switch to show dialog 1`] = ` -"[ - { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1920, - \\"height\\": 1080 - } - }, - { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": { - \\"type\\": \\"text/javascript\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 5 - } - ], - \\"id\\": 4 - } - ], - \\"id\\": 3 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 7 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"I'm a dialog\\", - \\"id\\": 9 - } - ], - \\"id\\": 8 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", - \\"id\\": 10 - } - ], - \\"id\\": 6 - } - ], - \\"id\\": 2 - } - ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 8, - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"modal\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 8, - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"non-modal\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - } -]" -`; - -exports[`dialog > switch to showModal dialog 1`] = ` -"[ - { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1920, - \\"height\\": 1080 - } - }, - { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": { - \\"type\\": \\"text/javascript\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 5 - } - ], - \\"id\\": 4 - } - ], - \\"id\\": 3 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 7 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"dialog\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"I'm a dialog\\", - \\"id\\": 9 - } - ], - \\"id\\": 8 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", - \\"id\\": 10 - } - ], - \\"id\\": 6 - } - ], - \\"id\\": 2 - } - ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 8, - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"non-modal\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 8, - \\"attributes\\": { - \\"open\\": \\"\\", - \\"rr_open_mode\\": \\"modal\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - } -]" -`; diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index 32aa4644eb..2cdf69f006 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -53,11 +53,7 @@ async function injectRecordScript( } catch (e) { // we get this error: `Protocol error (DOM.resolveNode): Node with given id does not belong to the document` // then the page wasn't loaded yet and we try again - if ( - !e.message.includes('DOM.resolveNode') || - !e.message.includes('DOM.describeNode') - ) - throw e; + if (!e.message.includes('DOM.resolveNode')) throw e; await injectRecordScript(frame, options); return; } @@ -242,7 +238,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should map scroll events correctly', async () => { @@ -265,7 +261,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); @@ -293,7 +289,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record DOM node removal', async () => { @@ -305,7 +301,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record DOM attribute changes', async () => { @@ -317,7 +313,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record DOM text changes', async () => { @@ -329,7 +325,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record canvas elements', async () => { @@ -346,7 +342,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should record custom events', async () => { @@ -362,7 +358,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('captures mutations on adopted stylesheets', async () => { @@ -425,7 +421,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('captures mutations on stylesheets', async () => { @@ -480,7 +476,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); @@ -509,7 +505,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); @@ -541,7 +537,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); it('should filter out forwarded cross origin rrweb messages', async () => { @@ -568,7 +564,7 @@ describe('cross origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); }); @@ -596,7 +592,7 @@ describe('same origin iframes', function (this: ISuite) { // two events (full snapshot + meta) from main frame, // and two (full snapshot + mutation) from iframe expect(events.length).toBe(4); - await assertSnapshot(events); + assertSnapshot(events); }); it('should record cross-origin iframe in same-origin iframe', async () => { @@ -620,6 +616,6 @@ describe('same origin iframes', function (this: ISuite) { const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; - await assertSnapshot(snapshots); + assertSnapshot(snapshots); }); }); diff --git a/packages/rrweb/test/record/dialog.test.ts b/packages/rrweb/test/record/dialog.test.ts deleted file mode 100644 index ab6542b547..0000000000 --- a/packages/rrweb/test/record/dialog.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { vi } from 'vitest'; - -import { - assertSnapshot, - getServerURL, - ISuite, - launchPuppeteer, - startServer, - waitForRAF, -} from '../utils'; -import { - attributeMutation, - EventType, - eventWithTime, - listenerHandler, -} from '@rrweb/types'; -import { recordOptions } from '../../src/types'; - -interface IWindow extends Window { - rrweb: { - record: ( - options: recordOptions, - ) => listenerHandler | undefined; - addCustomEvent(tag: string, payload: T): void; - }; - emit: (e: eventWithTime) => undefined; -} - -const attributeMutationFactory = ( - mutation: attributeMutation['attributes'], -) => { - return { - data: { - attributes: [ - { - attributes: mutation, - }, - ], - }, - }; -}; - -describe('dialog', () => { - vi.setConfig({ testTimeout: 100_000 }); - let code: ISuite['code']; - let page: ISuite['page']; - let browser: ISuite['browser']; - let server: ISuite['server']; - let serverURL: ISuite['serverURL']; - let events: ISuite['events']; - - beforeAll(async () => { - server = await startServer(); - serverURL = getServerURL(server); - browser = await launchPuppeteer(); - - const bundlePath = path.resolve(__dirname, '../../dist/rrweb.umd.cjs'); - code = fs.readFileSync(bundlePath, 'utf8'); - }); - - afterEach(async () => { - await page.close(); - }); - - afterAll(async () => { - await server.close(); - await browser.close(); - }); - - beforeEach(async () => { - page = await browser.newPage(); - page.on('console', (msg) => { - console.log(msg.text()); - }); - - await page.goto(`${serverURL}/html/dialog.html`); - await page.addScriptTag({ - path: path.resolve(__dirname, '../../dist/rrweb.umd.cjs'), - }); - await waitForRAF(page); - events = []; - - await page.exposeFunction('emit', (e: eventWithTime) => { - if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) { - return; - } - events.push(e); - }); - - page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); - - await page.evaluate(() => { - const { record } = (window as unknown as IWindow).rrweb; - record({ - emit: (window as unknown as IWindow).emit, - }); - }); - - await waitForRAF(page); - }); - - it('show dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.show(); - }); - - const lastEvent = events[events.length - 1]; - - expect(lastEvent).toMatchObject(attributeMutationFactory({ open: '' })); - // assertSnapshot(events); - }); - - it('showModal dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.showModal(); - }); - - const lastEvent = events[events.length - 1]; - - expect(lastEvent).toMatchObject( - attributeMutationFactory({ rr_open_mode: 'modal' }), - ); - }); - - it('showModal & close dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.showModal(); - }); - await waitForRAF(page); - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.close(); - }); - - const lastEvent = events[events.length - 1]; - - expect(lastEvent).toMatchObject(attributeMutationFactory({ open: null })); - }); - - it('show & close dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.show(); - }); - await waitForRAF(page); - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.close(); - }); - - const lastEvent = events[events.length - 1]; - - expect(lastEvent).toMatchObject(attributeMutationFactory({ open: null })); - }); - - it('switch to showModal dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.show(); - }); - await waitForRAF(page); - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.close(); - dialog.showModal(); - }); - - await assertSnapshot(events); - }); - - it('switch to show dialog', async () => { - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.showModal(); - }); - await waitForRAF(page); - await page.evaluate(() => { - const dialog = document.querySelector('dialog') as HTMLDialogElement; - dialog.close(); - dialog.show(); - }); - - await assertSnapshot(events); - }); - - it('add dialog and showModal', async () => { - await page.evaluate(() => { - const dialog = document.createElement('dialog') as HTMLDialogElement; - document.body.appendChild(dialog); - dialog.showModal(); - }); - await waitForRAF(page); - - await assertSnapshot(events); - }); - - it('add dialog and show', async () => { - await page.evaluate(() => { - const dialog = document.createElement('dialog') as HTMLDialogElement; - document.body.appendChild(dialog); - dialog.show(); - }); - await waitForRAF(page); - - await assertSnapshot(events); - }); - - // TODO: implement me in the future - it.skip('should record playback order with multiple dialogs opening', async () => { - await page.evaluate(() => { - const dialog1 = document.createElement('dialog') as HTMLDialogElement; - dialog1.className = 'dialog1'; - document.body.appendChild(dialog1); - const dialog2 = document.createElement('dialog') as HTMLDialogElement; - dialog1.className = 'dialog2'; - document.body.appendChild(dialog2); - dialog2.showModal(); // <== Note that dialog TWO is being triggered first - dialog1.showModal(); - }); - - await waitForRAF(page); - await assertSnapshot(events); // <== This should trigger showModal() on dialog2 first, then dialog1 - }); -}); diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index 87999c458f..c19023732a 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -124,7 +124,7 @@ describe('record webgl', function (this: ISuite) { ], }, }); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('will record changes to a webgl2 canvas element', async () => { @@ -150,7 +150,7 @@ describe('record webgl', function (this: ISuite) { ], }, }); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('will record changes to a canvas element before the canvas gets added', async () => { @@ -165,7 +165,7 @@ describe('record webgl', function (this: ISuite) { await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('will record changes to a canvas element before the canvas gets added (webgl2)', async () => { @@ -188,7 +188,7 @@ describe('record webgl', function (this: ISuite) { // we need to change this await waitForRAF(ctx.page); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('will record webgl variables', async () => { @@ -203,7 +203,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('will record webgl variables in reverse order', async () => { @@ -219,7 +219,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); }); it('sets _context on canvas.getContext()', async () => { @@ -264,7 +264,7 @@ describe('record webgl', function (this: ISuite) { await ctx.page.waitForTimeout(50); - await assertSnapshot(ctx.events); + assertSnapshot(ctx.events); expect(ctx.events.length).toEqual(5); }); @@ -312,7 +312,7 @@ describe('record webgl', function (this: ISuite) { await waitForRAF(ctx.page); // should yield a frame for each change at a max of 60fps - await assertSnapshot(stripBase64(ctx.events)); + assertSnapshot(stripBase64(ctx.events)); }); }); }); diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-closed-dialogs-show-nothing-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-closed-dialogs-show-nothing-1-snap.png deleted file mode 100644 index 9fb34401f4292e123559cdb06cd35f8842b1c146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10592 zcmeHNJxjw-6g{b-wxyAXOB986aqi$y@T|9VC|KJ$zW#XbJF4QUo5$~?m8*syeA$D&`@0i;T-MTOZ_ZRu z)A^by8!fABDt8xSrV2b|3QY2tAVEMfK9C_943ta;1Dk_^fz5%osrx2B4}1iBog23c$HZdmv+h_F#1FLD;RX z0>ugW7hrU6$(zw$2?DfN8UxxZ+AGll?G^2n@PPK}zwJRT?3MRl>hVC}jW(J=$4}0F E0Mb(D%HLOWaU07rnLQrBNgph=gr1vH>p6UEM|9E=dJvrxn@7??E`_|w6 z?sxOeVLxBvH#WS1Ac!&kfX}B0Vwi~_E8JGE0#BwNw(SF#Wu#Ai_aaq1i)RS35yAWH z`FjdYG@Rb?I9c63+t*kl_-)V4D7oq8I*cog(-g$*IubKrt=`1BIL(<}Ykd5+-M3CB zYwC}=H92niI*fJ7(6OTsx8e4&!J7BpTUQco{-7{c(DiAhfGbn&Yswgyf3T}>mYL?M zG2tmg=~8duXa${5td8t1>;$}k_P3xrr^l;(5wmY^f4)}#2w`__LJahD{@a<$^wh_z z+5hg|x*|+ZWv*N9pr>}PS!SlE{&f*C(o=``B2aA5-bbfn>lA??sP0flL8C$+3@D_`Dtr|8?EUI?*y*+jCd1j++QunI zf=PX+g>Hw^ChnU0si$CXg*)6f?&q>Bu6ts!zY#yo&1~DPTnT#iclTEt!uJafon(V6 zd$kFySlnkKfQHsf)1w4G}#R5BkZ4UZ;`RO-U1q*ipf0-a9ruTmlM(fCqvZyMZ_$?OUg#zI8cuS33L`$y zqMRy~J}7G4d7*71_%YH1+iO#1pE6~);<$Bvp{0XyEOh`yI#&vffM727G6QPVWPN7}MHGalI zEuNapoUl~R=2B>br=8z28BVL@`%ep(gHO_ai84wB*RVU~ zv)M!pK8i=f%AV8$l?0QxyU25SbQ;i1)M#sZqh@g?55pjHqpvjaG-{P|I49d^m6j4U z$Es2uvvfc6et7spE&VY|{UWYW+%z-ygN@e*_?no3nx&UnxU^upN;0$z?qagnL3?C| zFZQ>47uNFJw-dCBeR&q%z2wKgd`9j*G2$fa>AiKLHbKx89Do1a_14z>McGV6h*XHv zl1CIA*QnX2_k*g#_IS&F_P2~rmVm+1nxYn421PyNOwlYcRl^cq1y21eX0%lu5WOS# z≷|;!zgP+$E-?sCE8F8hNV3W@5%B-Vm;y4##0eJysmmQ+)MJyE_{xio4bcqv`TU zAhdF!fmP;}x^U1-THnyJgGh_t-^(D-6K8q_Et2#&jYiJ0PHcHG*g*XK`;SVBi#uW) zh6|0&T_*QgFCN~uZQG6Vpt_Tv71|TZPsFwC`lQH?3(P050Lt=De;$~N72Y^hnlM5Z zMoOo+%Eye1f=>Kk5_HywdC=<)&S3duKk8%yOC4+1I7ziVRz3E$+`d^Z)27pZAn`xE2K`;Hh`zE9}95J_02@L#rAUmZyq4Et7|?LRZ66U0a?V z!c&z}p>U;rN}D6m!e~`cV?tyUzq}0CdMcWrdNQHl0L>uRArK)qWS0p`oAGnhBIe{z z5<=>WIxKKzXrG@`&*#<|C~R;_{8Uc~>0UG=Lv}L{Ggu$n{jhxDS4>v!H9u-C-`k^e z{Car>>DiA(QD1zqIe9qoC^ce&lhdvawdt~CgAcLaFL4w>xdZ#e!_6f5aI#Rzr(^(@ z(jO5|-5I}>_VgWk$Bu>}s-bn7L&H!)w_~5j>9O4z%5H+y>=UJsj+2czgZN--)e^ob zOL3nmPGod*a?G6_9j^+#<&8EwB??8#$jeKvdH{2aT?0PM4zTo0agdztYo=m=cep)w z_DB}@4326RDhVual90#cm3dTzklN0#YDlVS?&of~-=; z&Uq@vE-0scac6&hi#K=p{(o+JcvDVi#cgZ~MA#t^62Q)nAR#bD1&AMULT%DZ)`;Dx?$$5uuzDG|qC&Nm{jB zm`7iV5Z{@&!jz;-x|!5)W%*8geE8nzb^t#l1P{MHj#m(ir<(nnp_Z zr)KBm2xuf;=etLPj0BX;(1DYY(%cG z5bsuCTpu&=G{F{#vxzl0UjSB6?Dne0nf%6?B!0OUcBZnHiqR_CaZUifvU4m|?C*T7 z$e85O^knG&M6a2kKt>#h{O50t4L2tf0Yz4fXGzbkP1{)N5_`?vWpNazVxKd>szkN^ zi}DRdtCXB9`<}`mMu*fcRfs28BS^|qTYx$JTM(nKpm(z;7Wj%%3;+*KJe|@NSWcDl zaoPX|L+S{Fh-6l`lhW`4G-yJ2(HGOqU#>1u*)lyR6?X}=&Y}LEHc zpdq7E!(RzQMER#K0Z=5s{{ojB++>-2!)wN4G_-di6PXkkvLhyMkK(}T4J>p@TE9!;-Y z@$nWwZd(D5;y`0xL}2Y3wBonfx+{Xbvm722gBG?IoKWc6!PQ}UB6E%IP!ZfXZ2HeX f@X^WsAD#JJu83GgI51ua6^#GL&!=i{*!e#Itmd7) diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-add-an-opened-dialog-with-show-modal-in-incremental-snapshot.png deleted file mode 100644 index f328c34b5b1b95a917d80f69c2fa391285891f97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12721 zcmeHNc~nzZ8h?t4wor?m7L?5;N?Vatfw0AeifI+3OjQ&JElZ{XB1D$R5?m^61sTIG zflO;FZAF4mKp>D%HLOWaU07rnLQrBNgph=gr1vH>p6UEM|9E=dJvrxn@7??E`_|w6 z?sxOeVLxBvH#WS1Ac!&kfX}B0Vwi~_E8JGE0#BwNw(SF#Wu#Ai_aaq1i)RS35yAWH z`FjdYG@Rb?I9c63+t*kl_-)V4D7oq8I*cog(-g$*IubKrt=`1BIL(<}Ykd5+-M3CB zYwC}=H92niI*fJ7(6OTsx8e4&!J7BpTUQco{-7{c(DiAhfGbn&Yswgyf3T}>mYL?M zG2tmg=~8duXa${5td8t1>;$}k_P3xrr^l;(5wmY^f4)}#2w`__LJahD{@a<$^wh_z z+5hg|x*|+ZWv*N9pr>}PS!SlE{&f*C(o=``B2aA5-bbfn>lA??sP0flL8C$+3@D_`Dtr|8?EUI?*y*+jCd1j++QunI zf=PX+g>Hw^ChnU0si$CXg*)6f?&q>Bu6ts!zY#yo&1~DPTnT#iclTEt!uJafon(V6 zd$kFySlnkKfQHsf)1w4G}#R5BkZ4UZ;`RO-U1q*ipf0-a9ruTmlM(fCqvZyMZ_$?OUg#zI8cuS33L`$y zqMRy~J}7G4d7*71_%YH1+iO#1pE6~);<$Bvp{0XyEOh`yI#&vffM727G6QPVWPN7}MHGalI zEuNapoUl~R=2B>br=8z28BVL@`%ep(gHO_ai84wB*RVU~ zv)M!pK8i=f%AV8$l?0QxyU25SbQ;i1)M#sZqh@g?55pjHqpvjaG-{P|I49d^m6j4U z$Es2uvvfc6et7spE&VY|{UWYW+%z-ygN@e*_?no3nx&UnxU^upN;0$z?qagnL3?C| zFZQ>47uNFJw-dCBeR&q%z2wKgd`9j*G2$fa>AiKLHbKx89Do1a_14z>McGV6h*XHv zl1CIA*QnX2_k*g#_IS&F_P2~rmVm+1nxYn421PyNOwlYcRl^cq1y21eX0%lu5WOS# z≷|;!zgP+$E-?sCE8F8hNV3W@5%B-Vm;y4##0eJysmmQ+)MJyE_{xio4bcqv`TU zAhdF!fmP;}x^U1-THnyJgGh_t-^(D-6K8q_Et2#&jYiJ0PHcHG*g*XK`;SVBi#uW) zh6|0&T_*QgFCN~uZQG6Vpt_Tv71|TZPsFwC`lQH?3(P050Lt=De;$~N72Y^hnlM5Z zMoOo+%Eye1f=>Kk5_HywdC=<)&S3duKk8%yOC4+1I7ziVRz3E$+`d^Z)27pZAn`xE2K`;Hh`zE9}95J_02@L#rAUmZyq4Et7|?LRZ66U0a?V z!c&z}p>U;rN}D6m!e~`cV?tyUzq}0CdMcWrdNQHl0L>uRArK)qWS0p`oAGnhBIe{z z5<=>WIxKKzXrG@`&*#<|C~R;_{8Uc~>0UG=Lv}L{Ggu$n{jhxDS4>v!H9u-C-`k^e z{Car>>DiA(QD1zqIe9qoC^ce&lhdvawdt~CgAcLaFL4w>xdZ#e!_6f5aI#Rzr(^(@ z(jO5|-5I}>_VgWk$Bu>}s-bn7L&H!)w_~5j>9O4z%5H+y>=UJsj+2czgZN--)e^ob zOL3nmPGod*a?G6_9j^+#<&8EwB??8#$jeKvdH{2aT?0PM4zTo0agdztYo=m=cep)w z_DB}@4326RDhVual90#cm3dTzklN0#YDlVS?&of~-=; z&Uq@vE-0scac6&hi#K=p{(o+JcvDVi#cgZ~MA#t^62Q)nAR#bD1&AMULT%DZ)`;Dx?$$5uuzDG|qC&Nm{jB zm`7iV5Z{@&!jz;-x|!5)W%*8geE8nzb^t#l1P{MHj#m(ir<(nnp_Z zr)KBm2xuf;=etLPj0BX;(1DYY(%cG z5bsuCTpu&=G{F{#vxzl0UjSB6?Dne0nf%6?B!0OUcBZnHiqR_CaZUifvU4m|?C*T7 z$e85O^knG&M6a2kKt>#h{O50t4L2tf0Yz4fXGzbkP1{)N5_`?vWpNazVxKd>szkN^ zi}DRdtCXB9`<}`mMu*fcRfs28BS^|qTYx$JTM(nKpm(z;7Wj%%3;+*KJe|@NSWcDl zaoPX|L+S{Fh-6l`lhW`4G-yJ2(HGOqU#>1u*)lyR6?X}=&Y}LEHc zpdq7E!(RzQMER#K0Z=5s{{ojB++>-2!)wN4G_-di6PXkkvLhyMkK(}T4J>p@TE9!;-Y z@$nWwZd(D5;y`0xL}2Y3wBonfx+{Xbvm722gBG?IoKWc6!PQ}UB6E%IP!ZfXZ2HeX f@X^WsAD#JJu83GgI51ua6^#GL&!=i{*!e#Itmd7) diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-close-dialog-again-when-open-attribute-gets-removed.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-close-dialog-again-when-open-attribute-gets-removed.png deleted file mode 100644 index 9fb34401f4292e123559cdb06cd35f8842b1c146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10592 zcmeHNJxjw-6g{b-wxyAXOB986aqi$y@T|9VC|KJ$zW#XbJF4QUo5$~?m8*syeA$D&`@0i;T-MTOZ_ZRu z)A^by8!fABDt8xSrV2b|3QY2tAVEMfK9C_943ta;1Dk_^fz5%osrx2B4}1iBog23c$HZdmv+h_F#1FLD;RX z0>ugW7hrU6$(zw$2?DfN8UxxZ+AGll?G^2n@PPK}zwJRT?3MRl>hVC}jW(J=$4}0F E0Mb(|e&6qW_ne##CIu}1!2SaSK^E`av2_oESY{!}yiHaMK#T18eSc6`r0xmuL#jJg z$`RyaWari`dyifk?c0}&(j40cC99kZKmX)&VsLhD#G3Nu9|j&QJiRxq`{YLpzc>(P ze_+2qCC>T0L$F6~-w~&!YjV$p+x@Pna_PKN^U8~MJidN|lD}hkf4_A`PFPHBp@$o} z%yP0_{AP+H5vRIoifYrYv4DUvK#`3X)9JHS1nF30@zHGk;}giD*_y2%0*4L6YBe%< zwsyic%VM_XXVrorU2RW`LIep%FFz4o+U#Z8X9@1hRMdnpW1WDvS93kK5Wy+|@z4?E12NYoeYx}Qi@}b^ zHCuo=t3Kt*%4?W>&^f}m3`6XaP!L9rMo}M=HY9I_d-eAGl^G9=Hl7;aJO^SOhJD)b z`dbV$jI}uiaYc<8TTMuQbr16}>xaLOZ4*8?X|0f!WmdEI)eE^aMWU?hD)7xf zwoNE1&S?&zH-7&bCoa>#rk3<5fZnV^z`N>)Er z(!{7e_~blyQcR_GUD>iWy5_*&zAbi(C$S@Iu{5O^WreameqsjXudGxzzcHVPb=rPb z##Dc^uDT+=j8WI0`NqW6^&g`?{N_VwR8drFdlByw&_@;p27St&fPz)*wPnZeVA$_n zV~#%~I5;>==!XhK00z)5g6{6G#9lys#FGoI9N+Upu-G%>unW1VF|NHB zweivh>kNIobWy3-F$}|MYHBdd+oXLi29XcZfniMjV9tn4R$(&g%5LkO2KJv^6>EBN{N^KbC#g-@$tG0FeaoUOa^*HF1LLI}2g=wXtpa^a(*2wU0}icK^YwgdZnkC}r*Y3*%OzDpp)m5!ft`UR zpRp%LdqZ+;9IKrI@_UY{`C%v;7#K)M8E63^XIPj@D&AP>ZPpJJ#a>(|}FOrflHaapeN_2#rRY(ApwM3)wxMBp4B}s1qZ{SoPDJCB3~7a~CWE zFZ-DF*NRI@Se5b~o0T3%WWh@8#DB8?4nU9!G+QozewCS6N z3fBfz?{7VY?%K5rFvwoPShy!3Ysq{#WQ`qM&U`szvTFsx@lGEyF>SPBMe@C?b%!J| zIZGU{bP1#0pi@hp55bPKbO_5X>eGuGt~rw~g_9W*GpzXN?DEtHPUY~COyT8Vo1r`5nMa`XMc)ip^n&(dNnJ#Mr7YLnh1+RdZ`S0iS-m`=On(Al9$4B?x!~yOK z+!c_$fy{j1%+&r5{EOob#~qHlnP|fo#{Xmo1}7+Bf%k1g2s#CaF%IKj*fqst7LQr5 zDfh3p*Kwe{M?ldyPLBG*{Tj--<_WK+XJF66dEVO-iIx-Z($|Z&Ei+z2kY#h;o~Tg# z-toY0E6VK6tc4bD4_Vf)es`dPE5mID(1Ak(LGW+_EQ7NK@*5$?BbHw}B8W{P{4c<7 zWXkw75d=>a5CqSN@GKIh#CWodCw>41c;bgAevl6E^Z-u}AP(^K08bAf4)FBg-%Ag! i{PPwi{*CcJk%-0ca}%kQH~$s{;m&QOt<`>%um1t{%+vn> diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal-in-full-snapshot.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-should-open-dialog-with-show-modal-in-full-snapshot.png deleted file mode 100644 index 679ab53ab6018a241532b95e3e29e22a1d15e676..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12505 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 WNin}nDi?M_AL92B_U7*)ef=|oQDxTv diff --git a/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-show-the-dialog-when-open-attribute-gets-added.png b/packages/rrweb/test/replay/__image_snapshots__/dialog-test-ts-test-replay-dialog-test-ts-dialog-show-the-dialog-when-open-attribute-gets-added.png deleted file mode 100644 index a3faffd85acf2672b5411a7b05ffea0471ffe11e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12445 zcmeHNYgAKL7QTq`P!N?Fg@T~vWebXeyb4KFRGI;TqYe}W6Dxv>pg;wYkT6xtIG|vl zLLfY>F42NeLEevmRUo{AA_j~Ql<+8z1PBm9Leg`fGi%nG>5mzwGyb^e&po;4-m~}F z``h2%=iFR7KycAq`O!)QK{WB7?>&ehsxb(%++Ixu+!^kua|Rcsu!Ampko-3NpAcjd zg5SIQ@I}U0PgG;d%tFH?XRA1k|JVJw#m)r3&d;9=5<1i9EW&()T8Nv@zF4YN`@W=X zZwJTxA2RN)&BOVr#qGCvq++!$z)a_N>#k*->TpWlo&NDgr?G@#`GRprTd6p=LAgfZ z!yA>B_j1HiwkBIzk^krFD6v2jr~;c&@BOz1OI;*J>l;L4sefP(0(Ay$%VtD*sh7Ma zMro<{2esOz4q~#LgdjSn*Kc!EQK`ZLI6l^Ij1G$ymVuu}*P(Qyu1+r^2Hez!(qeAbZ_WPKQo$QY z%sMCqwq5E9+?<&*V#MgFa6rR2e* zym`;T7J68Fu_1Y)&Mj@{C%n0OyQ%3852{YT8D3D&>Y0GqbL)4j4cqNU8{|IT|%P#=8GURe4q?Q$AB5;qv6SJfcSq;b}$*PF4Y# zk^TmhxwkD02NA|l4@e{34P60HFj10UG4j2B$kVmnC1<^9cEO`$mPrcV1vme^9UI8W zZ@s$VSZbtb@Z?z6Bkw{#mJ)|N{oF;=Bc#|RnR&E5y`~-3;cj=5lkY3d6StnDSPwD6 z2&QhSiE-O1wv@gJb$wMpRNN{KW-iR$o=Z0-E{u33858GUr1WwkA)4b4Lo7~|sHm!j zjXcO11mYTt*x2+8$B02os>HSA91r`L?LnRI&=2oA4m7~8w>-vhwXEJ*P&L&=;T&aB zqD|e?k|m;{fSHlb^6?UG=(FE@`N?GxXQ@vp%Y<$d=qX?-kC4wb1<7tNSiC8>M zUE7pD*PnnJ&f^fN(Uh57;=n56+>{FIrX&QkBjf9sAU$v=D<=_@R zi8Z8L+{TnmT^6hY^S7TlU#1GKt9>n*vd~QO6pK6=M{EHmsN_Z8+UA>fb=3y{fu?V} z3s1Uw=KDxWM#o(Qw=qU>Gv zWQ1t2mF^f33_>nHUf0_H6j#K?WfcI%5X!DV*(=Ld>_iZ9OVFFL9r(f%4L6Kg0659O z;bs5@Z9NgZj1{Za`_;$mI!n*>RM>D!f?R25c{Lb(D2P=5o{9_JRTo2O{0BLiP50c> zr+5l^L{>4c3M0yh=yiD9n`RSe1)?r~qob{3_*kQusaPOOxx6-YGe)9&x~*nwDGuHT zYYTm%ZsrF}E14;vW&{>g+gkg#Gy+?8Yw@K}QysjI(%*SaCJ`l7O=|S08j`nL*35lL zz|6daGz9@^4I#+N#6%U6>b}nNt+d4#o_hYB#TP$p0+iPT)S_$Z+4AQ{3)m2SfH(ao z-#v5QJ(t3ioxI^Nn92yL*Y&RW>NH@*q#$m|zzm1T&iAdHlJXQ-yw#bP_w~bCcF^t4 z^cd&o&SlW|zKr0~fUi-NAhd@KjE1N_L}LnUv3uBusY!xrp$pIY@Rf z;_+5MEU_EIyLO8ed@~&V5WwqJE(r4sZ4aj%!0oMFk4_|2t&KBtPy4DpKnc>$CYYXM z6SHJ|Gl_&v&4gCFn?z^|V`Eahh|xAoX^+tA()93K11<1$U!vLK^YP7=OegDrwicXx zG9lgo^BJJP-l6r+p*H%dFf-{NtiP_864GC!R9$Sb*){s)*qWC{X^7g*bEo3gZuAPlUPx zSm49ho}#WmU4dpYQ7s|9K(?T4e^)1S@00t=2q6a3B4$yNUv^;<~c+c`6`=5^}(c{SfL3Hh{ WNin}nDi?M_AL92B_U7*)ef=|oQDxTv diff --git a/packages/rrweb/test/replay/dialog.test.ts b/packages/rrweb/test/replay/dialog.test.ts deleted file mode 100644 index 7fc1ab99f2..0000000000 --- a/packages/rrweb/test/replay/dialog.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as fs from 'fs'; -import { toMatchImageSnapshot } from 'jest-image-snapshot'; -import * as path from 'path'; -import { vi } from 'vitest'; - -import dialogPlaybackEvents, { - closedFullSnapshotTime, - showIncrementalAttributeTime, - closeIncrementalAttributeTime, - showModalIncrementalAttributeTime, - showFullSnapshotTime, - showModalFullSnapshotTime, - showModalIncrementalAddTime, - switchBetweenShowModalAndShowIncrementalAttributeTime, - switchBetweenShowAndShowModalIncrementalAttributeTime, -} from '../events/dialog-playback'; -import { - fakeGoto, - getServerURL, - hideMouseAnimation, - ISuite, - launchPuppeteer, - startServer, - waitForRAF, -} from '../utils'; - -expect.extend({ toMatchImageSnapshot }); - -describe('dialog', () => { - vi.setConfig({ testTimeout: 100_000 }); - let code: ISuite['code']; - let page: ISuite['page']; - let browser: ISuite['browser']; - let server: ISuite['server']; - let serverURL: ISuite['serverURL']; - - beforeAll(async () => { - server = await startServer(); - serverURL = getServerURL(server); - browser = await launchPuppeteer(); - - const bundlePath = path.resolve(__dirname, '../../dist/rrweb.umd.cjs'); - code = fs.readFileSync(bundlePath, 'utf8'); - }); - - afterEach(async () => { - await page.close(); - }); - - afterAll(async () => { - await server.close(); - await browser.close(); - }); - - beforeEach(async () => { - page = await browser.newPage(); - page.on('console', (msg) => { - console.log(msg.text()); - }); - - await fakeGoto(page, `${serverURL}/html/dialog.html`); - await page.evaluate(code); - await waitForRAF(page); - await hideMouseAnimation(page); - }); - - [ - { - name: 'show the dialog when open attribute gets added', - time: showIncrementalAttributeTime, - }, - { - name: 'should close dialog again when open attribute gets removed', - time: closeIncrementalAttributeTime, - }, - { - name: 'should open dialog with showModal', - time: showModalIncrementalAttributeTime, - }, - { - name: 'should switch between showModal and show', - time: switchBetweenShowModalAndShowIncrementalAttributeTime, - }, - { - name: 'should switch between show and showModal', - time: switchBetweenShowAndShowModalIncrementalAttributeTime, - }, - { - name: 'should open dialog with show in full snapshot', - time: showFullSnapshotTime, - }, - { - name: 'should open dialog with showModal in full snapshot', - time: showModalFullSnapshotTime, - }, - { - name: 'should add an opened dialog with showModal in incremental snapshot', - time: showModalIncrementalAddTime, - }, - { - name: 'should add an opened dialog with showModal in incremental snapshot alternative', - time: [showModalFullSnapshotTime, showModalIncrementalAddTime], - }, - ].forEach(({ name, time }) => { - [true, false].forEach((useVirtualDom) => { - it(`${name} (virtual dom: ${useVirtualDom})`, async () => { - await page.evaluate( - `let events = ${JSON.stringify(dialogPlaybackEvents)}`, - ); - await page.evaluate(` - const { Replayer } = rrweb; - window.replayer = new Replayer(events, { useVirtualDom: ${useVirtualDom} }); - `); - const timeArray = Array.isArray(time) ? time : [time]; - for (let i = 0; i < timeArray.length; i++) { - await page.evaluate(` - window.replayer.pause(${timeArray[i]}); - `); - await waitForRAF(page); - } - - const frameImage = await page!.screenshot({ - fullPage: false, - }); - const defaultImageFilePrefix = - 'dialog-test-ts-test-replay-dialog-test-ts-dialog'; - const kebabCaseName = name - .replace(/ /g, '-') - .replace(/showModal/g, 'show-modal'); - const imageFileName = `${defaultImageFilePrefix}-${kebabCaseName}`; - expect(frameImage).toMatchImageSnapshot({ - customSnapshotIdentifier: imageFileName, - failureThreshold: 0.05, - failureThresholdType: 'percent', - dumpDiffToConsole: true, - storeReceivedOnFailure: true, - }); - }); - }); - }); - - it('closed dialogs show nothing', async () => { - await page.evaluate(`let events = ${JSON.stringify(dialogPlaybackEvents)}`); - await page.evaluate(` - const { Replayer } = rrweb; - window.replayer = new Replayer(events); - `); - await waitForRAF(page); - - const frameImage = await page!.screenshot(); - expect(frameImage).toMatchImageSnapshot({ - failureThreshold: 0.05, - failureThresholdType: 'percent', - }); - }); - - // TODO: implement me in the future - it.skip('should trigger showModal on multiple dialogs in a specific order'); -}); diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 3cdc05abd5..404357a799 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -18,8 +18,7 @@ import inputEvents from './events/input'; import iframeEvents from './events/iframe'; import selectionEvents from './events/selection'; import shadowDomEvents from './events/shadow-dom'; -import badTextareaEvents from './events/bad-textarea'; -import badStyleEvents from './events/bad-style'; +import textareaEvents from './events/bad-textarea'; import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; import adoptedStyleSheet from './events/adopted-style-sheet'; @@ -1143,7 +1142,7 @@ describe('replayer', function () { }); it('can deal with legacy duplicate/conflicting values on textareas', async () => { - await page.evaluate(`events = ${JSON.stringify(badTextareaEvents)}`); + await page.evaluate(`events = ${JSON.stringify(textareaEvents)}`); const displayValue = await page.evaluate(` const { Replayer } = rrweb; @@ -1156,25 +1155,4 @@ describe('replayer', function () { // If the custom element is defined, the display value will be 'block'. expect(displayValue).toEqual('this value is used for replay'); }); - - it('can deal with duplicate/conflicting values on style elements', async () => { - await page.evaluate(`events = ${JSON.stringify(badStyleEvents)}`); - - const changedColors = await page.evaluate(` - const { Replayer } = rrweb; - const replayer = new Replayer(events); - replayer.pause(1000); - // Get the color of the elements after applying the style mutation event - [ - replayer.iframe.contentWindow.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#one'), - ).color, - replayer.iframe.contentWindow.getComputedStyle( - replayer.iframe.contentDocument.querySelector('#two'), - ).color, - ]; -`); - const newColor = 'rgb(255, 255, 0)'; // yellow - expect(changedColors).toEqual([newColor, newColor]); - }); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 42c8961304..c7347d89c3 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -110,17 +110,9 @@ export function stringifySnapshots(snapshots: eventWithTime[]): string { snapshots .filter((s) => { if ( - // mouse move or viewport resize can happen on accidental user interference - // so we ignore them - (s.type === EventType.IncrementalSnapshot && - (s.data.source === IncrementalSource.MouseMove || - s.data.source === IncrementalSource.ViewportResize)) || - // ignore '[vite] connected' messages from vite - (s.type === EventType.Plugin && - s.data.plugin === 'rrweb/console@1' && - (s.data.payload as { payload: string[] })?.payload?.find((msg) => - msg.includes('[vite] connected'), - )) + s.type === EventType.IncrementalSnapshot && + (s.data.source === IncrementalSource.MouseMove || + s.data.source === IncrementalSource.ViewportResize) ) { return false; } @@ -250,18 +242,18 @@ export function stringifySnapshots(snapshots: eventWithTime[]): string { function stripBlobURLsFromAttributes(node: { attributes: { - [key: string]: any; + src?: string; }; }) { - for (const attr in node.attributes) { - if ( - typeof node.attributes[attr] === 'string' && - node.attributes[attr].startsWith('blob:') - ) { - node.attributes[attr] = node.attributes[attr] - .replace(/[\w-]+$/, '...') - .replace(/:[0-9]+\//, ':xxxx/'); - } + if ( + 'src' in node.attributes && + node.attributes.src && + typeof node.attributes.src === 'string' && + node.attributes.src.startsWith('blob:') + ) { + node.attributes.src = node.attributes.src + .replace(/[\w-]+$/, '...') + .replace(/:[0-9]+\//, ':xxxx/'); } } diff --git a/packages/rrweb/tsconfig.json b/packages/rrweb/tsconfig.json index d27cd53179..6a8a5ffb3d 100644 --- a/packages/rrweb/tsconfig.json +++ b/packages/rrweb/tsconfig.json @@ -9,6 +9,7 @@ "vite/client", "@types/dom-mediacapture-transform", "@types/offscreencanvas", + // rrweb specific: /* * @see https://vitest.dev/config/#globals @@ -17,6 +18,7 @@ */ "vitest/globals" ], + // TODO: enable me in the future, this is quite a large project // at time of writing (April 2024) there are over 100 errors in rrweb "strict": false @@ -25,9 +27,6 @@ { "path": "../types" }, - { - "path": "../utils" - }, { "path": "../rrdom" }, diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index b0e111d212..3980e28919 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,23 @@ # @rrweb/types +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index f95bff061d..2ca0a1c642 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-types", - "version": "2.0.13", + "version": "2.0.15", "publishConfig": { "access": "public" }, @@ -10,9 +10,9 @@ ], "scripts": { "dev": "vite build --watch", - "build": "yarn turbo run prepublish", + "build": "tsc -noEmit && vite build", "check-types": "tsc -noEmit", - "prepublish": "tsc -noEmit && vite build", + "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types#readme", @@ -46,11 +46,19 @@ "package.json" ], "devDependencies": { - "vite": "^5.3.1", - "vite-plugin-dts": "^3.9.1" + "vite": "^5.2.8", + "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-snapshot": "^2.0.13" + "@changesets/cli": "^2.27.1", + "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", + "@saola.ai/rrweb-snapshot": "^2.0.15", + "browserslist": "^4.22.1", + "concurrently": "^7.1.0", + "cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "eslint": "^8.53.0", + "eslint-plugin-jest": "^27.6.0", + "turbo": "^2.0.3" }, "browserslist": [ "supports es6-class" diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md deleted file mode 100644 index 6fd00c059f..0000000000 --- a/packages/utils/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# @rrweb/utils - -## 2.0.13 - -### Patch Changes - -- Merge from rrweb remote upstream - -## 2.0.0-alpha.17 - -### Patch Changes - -- [#1509](https://github.com/rrweb-io/rrweb/pull/1509) [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414) Thanks [@Juice10](https://github.com/Juice10)! - Reverse monkey patch built in methods to support LWC (and other frameworks like angular which monkey patch built in methods). diff --git a/packages/utils/Readme.md b/packages/utils/Readme.md deleted file mode 100644 index 2107a2f228..0000000000 --- a/packages/utils/Readme.md +++ /dev/null @@ -1,178 +0,0 @@ -# @rrweb/utils - -This package contains the shared utility functions used across rrweb packages. -See the [guide](../../guide.md) for more info on rrweb. - -## Sponsors - -[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site. - -### Gold Sponsors 🥇 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Silver Sponsors 🥈 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Bronze Sponsors 🥉 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Backers - - - -## Core Team Members - - - - - - - - -
- - -
Yuyz0112 -

-
-
- - -
Yun Feng -

-
-
- - -
eoghanmurray -

-
-
- - -
Juice10 -
open for rrweb consulting -
-
- -## Who's using rrweb? - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - Smart screen recording for SaaS - -
- - The first ever UX automation tool - - - - Remote Access & Co-Browsing - - - - The open source, fullstack Monitoring Platform. - - - - Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions. - -
- - Intercept, Modify, Record & Replay HTTP Requests. - - - - In-app bug reporting & customer feedback platform. - - - - Self-hosted website analytics with heatmaps and session recordings. - - - - Interactive product demos for small marketing teams - -
diff --git a/packages/utils/package.json b/packages/utils/package.json deleted file mode 100644 index 4df05440ad..0000000000 --- a/packages/utils/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@saola.ai/rrweb-utils", - "version": "2.0.13", - "publishConfig": { - "access": "public" - }, - "keywords": [ - "rrweb", - "@rrweb/utils" - ], - "scripts": { - "dev": "vite build --watch", - "build": "tsc -noEmit && vite build", - "check-types": "tsc -noEmit", - "prepublish": "npm run build", - "lint": "yarn eslint src/**/*.ts" - }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/utils#readme", - "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" - }, - "license": "MIT", - "type": "module", - "main": "./dist/rrweb-utils.umd.cjs", - "module": "./dist/rrweb-utils.js", - "unpkg": "./dist/rrweb-utils.umd.cjs", - "typings": "dist/index.d.ts", - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/rrweb-utils.js" - }, - "require": { - "types": "./dist/index.d.cts", - "default": "./dist/rrweb-utils.umd.cjs" - } - } - }, - "files": [ - "dist", - "package.json" - ], - "devDependencies": { - "vite": "^5.2.8", - "vite-plugin-dts": "^3.8.1" - }, - "dependencies": {} -} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts deleted file mode 100644 index b88d7f452e..0000000000 --- a/packages/utils/src/index.ts +++ /dev/null @@ -1,221 +0,0 @@ -type PrototypeOwner = Node | ShadowRoot | MutationObserver | Element; -type TypeofPrototypeOwner = - | typeof Node - | typeof ShadowRoot - | typeof MutationObserver - | typeof Element; - -type BasePrototypeCache = { - Node: typeof Node.prototype; - ShadowRoot: typeof ShadowRoot.prototype; - MutationObserver: typeof MutationObserver.prototype; - Element: typeof Element.prototype; -}; - -const testableAccessors = { - Node: ['childNodes', 'parentNode', 'parentElement', 'textContent'] as const, - ShadowRoot: ['host', 'styleSheets'] as const, - Element: ['shadowRoot', 'querySelector', 'querySelectorAll'] as const, - MutationObserver: [] as const, -} as const; - -const testableMethods = { - Node: ['contains', 'getRootNode'] as const, - ShadowRoot: ['getSelection'], - Element: [], - MutationObserver: ['constructor'], -} as const; - -const untaintedBasePrototype: Partial = {}; - -export function getUntaintedPrototype( - key: T, -): BasePrototypeCache[T] { - if (untaintedBasePrototype[key]) - return untaintedBasePrototype[key] as BasePrototypeCache[T]; - - const defaultObj = globalThis[key] as TypeofPrototypeOwner; - const defaultPrototype = defaultObj.prototype as BasePrototypeCache[T]; - - // use list of testable accessors to check if the prototype is tainted - const accessorNames = - key in testableAccessors ? testableAccessors[key] : undefined; - const isUntaintedAccessors = Boolean( - accessorNames && - // @ts-expect-error 2345 - accessorNames.every((accessor: keyof typeof defaultPrototype) => - Boolean( - Object.getOwnPropertyDescriptor(defaultPrototype, accessor) - ?.get?.toString() - .includes('[native code]'), - ), - ), - ); - - const methodNames = key in testableMethods ? testableMethods[key] : undefined; - const isUntaintedMethods = Boolean( - methodNames && - methodNames.every( - // @ts-expect-error 2345 - (method: keyof typeof defaultPrototype) => - typeof defaultPrototype[method] === 'function' && - defaultPrototype[method]?.toString().includes('[native code]'), - ), - ); - - if (isUntaintedAccessors && isUntaintedMethods) { - untaintedBasePrototype[key] = defaultObj.prototype as BasePrototypeCache[T]; - return defaultObj.prototype as BasePrototypeCache[T]; - } - - try { - const iframeEl = document.createElement('iframe'); - document.body.appendChild(iframeEl); - const win = iframeEl.contentWindow; - if (!win) return defaultObj.prototype as BasePrototypeCache[T]; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - const untaintedObject = (win as any)[key] - .prototype as BasePrototypeCache[T]; - // cleanup - document.body.removeChild(iframeEl); - - if (!untaintedObject) return defaultPrototype; - - return (untaintedBasePrototype[key] = untaintedObject); - } catch { - return defaultPrototype; - } -} - -const untaintedAccessorCache: Record< - string, - (this: PrototypeOwner, ...args: unknown[]) => unknown -> = {}; - -export function getUntaintedAccessor< - K extends keyof BasePrototypeCache, - T extends keyof BasePrototypeCache[K], ->( - key: K, - instance: BasePrototypeCache[K], - accessor: T, -): BasePrototypeCache[K][T] { - const cacheKey = `${key}.${String(accessor)}`; - if (untaintedAccessorCache[cacheKey]) - return untaintedAccessorCache[cacheKey].call( - instance, - ) as BasePrototypeCache[K][T]; - - const untaintedPrototype = getUntaintedPrototype(key); - // eslint-disable-next-line @typescript-eslint/unbound-method - const untaintedAccessor = Object.getOwnPropertyDescriptor( - untaintedPrototype, - accessor, - )?.get; - - if (!untaintedAccessor) return instance[accessor]; - - untaintedAccessorCache[cacheKey] = untaintedAccessor; - - return untaintedAccessor.call(instance) as BasePrototypeCache[K][T]; -} - -type BaseMethod = ( - this: BasePrototypeCache[K], - ...args: unknown[] -) => unknown; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const untaintedMethodCache: Record> = {}; -export function getUntaintedMethod< - K extends keyof BasePrototypeCache, - T extends keyof BasePrototypeCache[K], ->( - key: K, - instance: BasePrototypeCache[K], - method: T, -): BasePrototypeCache[K][T] { - const cacheKey = `${key}.${String(method)}`; - if (untaintedMethodCache[cacheKey]) - return untaintedMethodCache[cacheKey].bind( - instance, - ) as BasePrototypeCache[K][T]; - - const untaintedPrototype = getUntaintedPrototype(key); - const untaintedMethod = untaintedPrototype[method]; - - if (typeof untaintedMethod !== 'function') return instance[method]; - - untaintedMethodCache[cacheKey] = untaintedMethod as BaseMethod; - - return untaintedMethod.bind(instance) as BasePrototypeCache[K][T]; -} - -export function childNodes(n: Node): NodeListOf { - return getUntaintedAccessor('Node', n, 'childNodes'); -} - -export function parentNode(n: Node): ParentNode | null { - return getUntaintedAccessor('Node', n, 'parentNode'); -} - -export function parentElement(n: Node): HTMLElement | null { - return getUntaintedAccessor('Node', n, 'parentElement'); -} - -export function textContent(n: Node): string | null { - return getUntaintedAccessor('Node', n, 'textContent'); -} - -export function contains(n: Node, other: Node): boolean { - return getUntaintedMethod('Node', n, 'contains')(other); -} - -export function getRootNode(n: Node): Node { - return getUntaintedMethod('Node', n, 'getRootNode')(); -} - -export function host(n: ShadowRoot): Element | null { - if (!n || !('host' in n)) return null; - return getUntaintedAccessor('ShadowRoot', n, 'host'); -} - -export function styleSheets(n: ShadowRoot): StyleSheetList { - return n.styleSheets; -} - -export function shadowRoot(n: Node): ShadowRoot | null { - if (!n || !('shadowRoot' in n)) return null; - return getUntaintedAccessor('Element', n as Element, 'shadowRoot'); -} - -export function querySelector(n: Element, selectors: string): Element | null { - return getUntaintedAccessor('Element', n, 'querySelector')(selectors); -} - -export function querySelectorAll( - n: Element, - selectors: string, -): NodeListOf { - return getUntaintedAccessor('Element', n, 'querySelectorAll')(selectors); -} - -export function mutationObserverCtor(): (typeof MutationObserver)['prototype']['constructor'] { - return getUntaintedPrototype('MutationObserver').constructor; -} - -export default { - childNodes, - parentNode, - parentElement, - textContent, - contains, - getRootNode, - host, - styleSheets, - shadowRoot, - querySelector, - querySelectorAll, - mutationObserver: mutationObserverCtor, -}; diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json deleted file mode 100644 index 1902007d56..0000000000 --- a/packages/utils/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": ["src"], - "exclude": ["vite.config.ts"], - "compilerOptions": { - "rootDir": "src", - "tsBuildInfoFile": "./tsconfig.tsbuildinfo" - }, - "references": [] -} diff --git a/packages/utils/vite.config.js b/packages/utils/vite.config.js deleted file mode 100644 index 854f2b9ef0..0000000000 --- a/packages/utils/vite.config.js +++ /dev/null @@ -1,4 +0,0 @@ -import path from 'path'; -import config from '../../vite.config.default'; - -export default config(path.resolve(__dirname, 'src/index.ts'), 'rrwebUtils'); diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index d35d11aedb..6eb3e451e3 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,25 @@ # @rrweb/web-extension +## 2.0.15 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.15 + - @saola.ai/rrweb@2.0.15 + +## 2.0.14 + +### Patch Changes + +- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.14 + - @saola.ai/rrweb@2.0.14 + ## 2.0.13 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 9179a294be..7f8233bd67 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@saola.ai/rrweb-web-extension", "private": true, - "version": "2.0.13", + "version": "2.0.15", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -18,12 +18,13 @@ "prepublish": "yarn build" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.13", + "@saola.ai/rrweb-types": "^2.0.15", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^4.2.1", + "cross-env": "^7.0.3", "type-fest": "^2.19.0", - "vite": "^5.3.1", + "vite": "^5.2.8", "vite-plugin-web-extension": "^4.1.3", "vite-plugin-zip-pack": "^1.2.2", "webextension-polyfill": "^0.10.0" @@ -41,7 +42,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "@saola.ai/rrweb": "^2.0.13", - "@saola.ai/rrweb-player": "^2.0.13" + "@saola.ai/rrweb": "^2.0.15", + "@saola.ai/rrweb-player": "^2.0.15" } } diff --git a/packages/web-extension/src/background/index.ts b/packages/web-extension/src/background/index.ts index 4ffda7827c..9a0a7c58b3 100644 --- a/packages/web-extension/src/background/index.ts +++ b/packages/web-extension/src/background/index.ts @@ -2,11 +2,11 @@ import Browser from 'webextension-polyfill'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import Channel from '~/utils/channel'; import { - type LocalData, + LocalData, LocalDataKey, RecorderStatus, - type Settings, - type SyncData, + Settings, + SyncData, SyncDataKey, } from '~/types'; import { pauseRecording, resumeRecording } from '~/utils/recording'; diff --git a/packages/web-extension/src/components/CircleButton.tsx b/packages/web-extension/src/components/CircleButton.tsx index f114a1cb66..abbb68e576 100644 --- a/packages/web-extension/src/components/CircleButton.tsx +++ b/packages/web-extension/src/components/CircleButton.tsx @@ -1,4 +1,4 @@ -import { Button, type ButtonProps } from '@chakra-ui/react'; +import { Button, ButtonProps } from '@chakra-ui/react'; interface CircleButtonProps extends ButtonProps { diameter: number; diff --git a/packages/web-extension/src/components/SidebarWithHeader.tsx b/packages/web-extension/src/components/SidebarWithHeader.tsx index 7895efe613..bcc07147ff 100644 --- a/packages/web-extension/src/components/SidebarWithHeader.tsx +++ b/packages/web-extension/src/components/SidebarWithHeader.tsx @@ -12,8 +12,8 @@ import { Drawer, DrawerContent, useDisclosure, - type BoxProps, - type FlexProps, + BoxProps, + FlexProps, Heading, Stack, Text, diff --git a/packages/web-extension/src/content/index.ts b/packages/web-extension/src/content/index.ts index 673f8ad577..18b35b2a31 100644 --- a/packages/web-extension/src/content/index.ts +++ b/packages/web-extension/src/content/index.ts @@ -1,16 +1,16 @@ -import Browser, { type Storage } from 'webextension-polyfill'; +import Browser, { Storage } from 'webextension-polyfill'; import { nanoid } from 'nanoid'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import { - type LocalData, + LocalData, LocalDataKey, RecorderStatus, ServiceName, - type Session, - type RecordStartedMessage, - type RecordStoppedMessage, + Session, + RecordStartedMessage, + RecordStoppedMessage, MessageName, - type EmitEventMessage, + EmitEventMessage, } from '~/types'; import Channel from '~/utils/channel'; import { isInCrossOriginIFrame } from '~/utils'; diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts index ded4a76422..3ac619e642 100644 --- a/packages/web-extension/src/content/inject.ts +++ b/packages/web-extension/src/content/inject.ts @@ -1,7 +1,7 @@ import { record } from '@saola.ai/rrweb'; import type { recordOptions } from '@saola.ai/rrweb'; import type { eventWithTime } from '@saola.ai/rrweb-types'; -import { MessageName, type RecordStartedMessage } from '~/types'; +import { MessageName, RecordStartedMessage } from '~/types'; import { isInCrossOriginIFrame } from '~/utils'; /** diff --git a/packages/web-extension/src/pages/SessionList.tsx b/packages/web-extension/src/pages/SessionList.tsx index d285932029..8aead1834f 100644 --- a/packages/web-extension/src/pages/SessionList.tsx +++ b/packages/web-extension/src/pages/SessionList.tsx @@ -23,15 +23,15 @@ import { useReactTable, flexRender, getCoreRowModel, - type SortingState, + SortingState, getSortedRowModel, - type PaginationState, + PaginationState, } from '@tanstack/react-table'; import { VscTriangleDown, VscTriangleUp } from 'react-icons/vsc'; import { useNavigate } from 'react-router-dom'; -import { type Session, EventName } from '~/types'; +import { Session, EventName } from '~/types'; import Channel from '~/utils/channel'; -import { deleteSessions, getAllSessions, downloadSessions } from '~/utils/storage'; +import { deleteSessions, getAllSessions } from '~/utils/storage'; import { FiChevronLeft, FiChevronRight, @@ -292,38 +292,24 @@ export function SessionList() { ))} {Object.keys(rowSelection).length > 0 && ( - - - - + )} diff --git a/packages/web-extension/src/popup/App.tsx b/packages/web-extension/src/popup/App.tsx index c83a03ced4..a6bed7b3f2 100644 --- a/packages/web-extension/src/popup/App.tsx +++ b/packages/web-extension/src/popup/App.tsx @@ -10,13 +10,16 @@ import { } from '@chakra-ui/react'; import { FiSettings, FiList, FiPause, FiPlay } from 'react-icons/fi'; import Channel from '~/utils/channel'; -import type { +import { LocalData, + LocalDataKey, + RecorderStatus, + ServiceName, RecordStartedMessage, RecordStoppedMessage, Session, + EventName, } from '~/types'; -import { LocalDataKey, RecorderStatus, ServiceName, EventName } from '~/types'; import Browser from 'webextension-polyfill'; import { CircleButton } from '~/components/CircleButton'; import { Timer } from './Timer'; @@ -36,8 +39,9 @@ export function App() { void Browser.storage.local.get(LocalDataKey.recorderStatus).then((data) => { const localData = data as LocalData; if (!localData || !localData[LocalDataKey.recorderStatus]) return; - const { status, startTimestamp, pausedTimestamp } = - localData[LocalDataKey.recorderStatus]; + const { status, startTimestamp, pausedTimestamp } = localData[ + LocalDataKey.recorderStatus + ]; setStatus(status); if (startTimestamp && pausedTimestamp) setStartTime(Date.now() - pausedTimestamp + startTimestamp || 0); diff --git a/packages/web-extension/src/utils/channel.ts b/packages/web-extension/src/utils/channel.ts index fc090bcc7f..1a8e9b2a82 100644 --- a/packages/web-extension/src/utils/channel.ts +++ b/packages/web-extension/src/utils/channel.ts @@ -1,5 +1,5 @@ import mitt from 'mitt'; -import Browser, { type Runtime } from 'webextension-polyfill'; +import Browser, { Runtime } from 'webextension-polyfill'; export type Message = EventType | ServiceType; export type EventType = { diff --git a/packages/web-extension/src/utils/recording.ts b/packages/web-extension/src/utils/recording.ts index 832c5a58fd..96f77c5da7 100644 --- a/packages/web-extension/src/utils/recording.ts +++ b/packages/web-extension/src/utils/recording.ts @@ -2,11 +2,11 @@ import Browser from 'webextension-polyfill'; import type { eventWithTime } from '@saola.ai/rrweb-types'; import { - type LocalData, + LocalData, LocalDataKey, RecorderStatus, - type RecordStartedMessage, - type RecordStoppedMessage, + RecordStartedMessage, + RecordStoppedMessage, ServiceName, } from '~/types'; import type Channel from './channel'; diff --git a/packages/web-extension/src/utils/storage.ts b/packages/web-extension/src/utils/storage.ts index e5fc05ab26..6c4c460ac8 100644 --- a/packages/web-extension/src/utils/storage.ts +++ b/packages/web-extension/src/utils/storage.ts @@ -88,22 +88,3 @@ export async function deleteSessions(ids: string[]) { return Promise.all([eventTransition.done, sessionTransition.done]); }); } - -export async function downloadSessions(ids: string[]) { - for (const sessionId of ids) { - const events = await getEvents(sessionId); - const session = await getSession(sessionId); - const blob = new Blob([JSON.stringify({ session, events }, null, 2)], { - type: 'application/json', - }); - - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${session.name}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - } -} diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index 2ca58feab9..0fd0602706 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -5,8 +5,6 @@ import * as path from 'path'; import type { PackageJson } from 'type-fest'; import react from '@vitejs/plugin-react'; -const emptyOutDir = !process.argv.includes('--watch'); - function useSpecialFormat( entriesToUse: string[], format: LibraryFormats, @@ -48,7 +46,7 @@ export default defineConfig({ 'dist', process.env.TARGET_BROWSER as string, ), - emptyOutDir, + emptyOutDir: true, }, // Add the webExtension plugin plugins: [ diff --git a/tsconfig.base.json b/tsconfig.base.json index d01ad45552..6ca3030761 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -14,7 +14,7 @@ "sourceMap": true, "skipLibCheck": true, "declaration": true, - "verbatimModuleSyntax": true, + "importsNotUsedAsValues": "error", "strict": true, "removeComments": true, "noImplicitAny": true, diff --git a/turbo.json b/turbo.json index 54bd1b278f..a48aa0dfc4 100644 --- a/turbo.json +++ b/turbo.json @@ -7,10 +7,9 @@ "vite.config.defaults.ts", "tsconfig.json" ], - "globalPassThroughEnv": ["PUPPETEER_HEADLESS"], "tasks": { "prepublish": { - "dependsOn": ["^prepublish", "//#references:update"], + "dependsOn": ["^prepublish"], "outputs": [ "lib/**", "es/**", @@ -21,30 +20,24 @@ ] }, "test": { - "dependsOn": ["^prepublish"] + "dependsOn": ["^prepublish"], + "passThroughEnv": ["PUPPETEER_HEADLESS"] }, "test:watch": { "persistent": true, - "cache": false + "passThroughEnv": ["PUPPETEER_HEADLESS"] }, "test:update": { - "dependsOn": ["^prepublish"] + "dependsOn": ["^prepublish"], + "passThroughEnv": ["PUPPETEER_HEADLESS"] }, "dev": { - "dependsOn": ["prepublish", "^prepublish"], + // "dependsOn": ["^prepublish"], "persistent": true, - "cache": false + "cache": false, + "passThroughEnv": ["CLEAR_DIST_DIR"] }, "lint": {}, - "check-types": { - "dependsOn": ["^prepublish"] - }, - "//#references:update": { - "inputs": ["packages/*/package.json", "packages/plugins/*/package.json"], - "outputs": [ - "packages/*/tsconfig.json", - "packages/plugins/*/tsconfig.json" - ] - } + "check-types": {} } } diff --git a/vite.config.default.ts b/vite.config.default.ts index 4dd3cc496e..9bfb2fd017 100644 --- a/vite.config.default.ts +++ b/vite.config.default.ts @@ -7,8 +7,7 @@ import { build, Format } from 'esbuild'; import { resolve } from 'path'; import { umdWrapper } from 'esbuild-plugin-umd-wrapper'; -// don't empty out dir if --watch flag is passed -const emptyOutDir = !process.argv.includes('--watch'); +const emptyOutDir = process.env.CLEAR_DIST_DIR !== 'false'; function minifyAndUMDPlugin({ name, diff --git a/yarn.lock b/yarn.lock index f68b2f59ab..7a00f1d5ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1956,16 +1956,11 @@ globby "^11.0.0" read-yaml-file "^1.1.0" -"@mdn/browser-compat-data@^5.2.34": +"@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13": version "5.5.33" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.5.33.tgz#c1177469bc4d39fa24c2cd3df317039e2b465b4c" integrity sha512-uO4uIBFn9D4UNyUmaueIWnE/IJhBlSJ7W1rANvDdaawhTX8CSgqUX8tj9/6a+1WjpL9Bgirf67d//S2VwDsfig== -"@mdn/browser-compat-data@^5.5.19": - version "5.6.12" - resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.6.12.tgz#ac3e1855c2387334bbfdb2b6249dd95c9d9c2b70" - integrity sha512-W/Km+GFczwpoimaXbtHYdjK26VHGszOEZ9EnIyLS2E65x6LEZs7r0FovR/XSkzgNau95sTxI3JfFKQFLIJE7EQ== - "@microsoft/api-extractor-model@7.28.13": version "7.28.13" resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz#96fbc52155e0d07e0eabbd9699065b77702fe33a" @@ -2185,85 +2180,170 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27" integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== +"@rollup/rollup-android-arm-eabi@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz#3e7eda4c0c1de6d2415343002d742ff95e38dca7" + integrity sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA== + "@rollup/rollup-android-arm64@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== +"@rollup/rollup-android-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz#04f679231acf7284f1f8a1f7250d0e0944865ba8" + integrity sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg== + "@rollup/rollup-darwin-arm64@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== +"@rollup/rollup-darwin-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz#ecea723041621747d0772af93b54752edf26467a" + integrity sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg== + "@rollup/rollup-darwin-x64@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== +"@rollup/rollup-darwin-x64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz#28e6e0687092f31e20982fc104779d48c643fc21" + integrity sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA== + +"@rollup/rollup-freebsd-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz#99e9173b8aef3d1ef086983da70413988206e530" + integrity sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g== + +"@rollup/rollup-freebsd-x64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz#f3a1ef941f8d3c6b2b036484c69a7b2d3d9ebbd7" + integrity sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw== + "@rollup/rollup-linux-arm-gnueabihf@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8" integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA== +"@rollup/rollup-linux-arm-gnueabihf@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz#9ba6adcc33f26f2a0c6ee658f0bbda4de8da2f75" + integrity sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA== + "@rollup/rollup-linux-arm-musleabihf@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549" integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== +"@rollup/rollup-linux-arm-musleabihf@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz#62f2426fa9016ec884f4fa779d7b62d5ba02a41a" + integrity sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ== + "@rollup/rollup-linux-arm64-gnu@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577" integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== +"@rollup/rollup-linux-arm64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz#f98ec111a231d35e0c6d3404e3d80f67f9d5b9f8" + integrity sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A== + "@rollup/rollup-linux-arm64-musl@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c" integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== +"@rollup/rollup-linux-arm64-musl@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz#4b36ffb8359f959f2c29afd187603c53368b6723" + integrity sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw== + "@rollup/rollup-linux-powerpc64le-gnu@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf" integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA== +"@rollup/rollup-linux-powerpc64le-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz#52f4b39e6783505d168a745b79d86474fde71680" + integrity sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA== + "@rollup/rollup-linux-riscv64-gnu@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9" integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== +"@rollup/rollup-linux-riscv64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz#49195be7e6a7d68d482b12461e2ea914e31ff977" + integrity sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA== + "@rollup/rollup-linux-s390x-gnu@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec" integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg== +"@rollup/rollup-linux-s390x-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz#4b8d50a205eac7b46cdcb9c50d4a6ae5994c02e0" + integrity sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ== + "@rollup/rollup-linux-x64-gnu@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942" integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== +"@rollup/rollup-linux-x64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz#dfcceebc5ccac7fc2db19471996026258c81b55f" + integrity sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig== + "@rollup/rollup-linux-x64-musl@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d" integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg== +"@rollup/rollup-linux-x64-musl@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz#192f78bad8429711d63a31dc0a7d3312e2df850e" + integrity sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ== + "@rollup/rollup-win32-arm64-msvc@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf" integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== +"@rollup/rollup-win32-arm64-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz#f4ec076579634f780b4e5896ae7f59f3e38e0c60" + integrity sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww== + "@rollup/rollup-win32-ia32-msvc@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54" integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg== +"@rollup/rollup-win32-ia32-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz#5458eab1929827e4f805cefb90bd09ecf7eeed2b" + integrity sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg== + "@rollup/rollup-win32-x64-msvc@4.18.0": version "4.18.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== -"@rrweb/utils@^2.0.0-alpha.17": - version "2.0.0-alpha.17" - resolved "https://registry.yarnpkg.com/@rrweb/utils/-/utils-2.0.0-alpha.17.tgz#d13a7326af0311e0f54551e223ace987608eaed5" - integrity sha512-HCsasPERBwOS9/LQeOytO2ETKTCqRj1wORBuxiy3t41hKhmi225DdrUPiWnyDdTQm1GdVbOymMRknJVPnZaSXw== +"@rollup/rollup-win32-x64-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz#93415e7e707e4b156d77c5950b983b58f4bc33f3" + integrity sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg== "@rushstack/node-core-library@4.0.2": version "4.0.2" @@ -2512,6 +2592,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/fs-extra@11.0.1": version "11.0.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.1.tgz#f542ec47810532a8a252127e6e105f487e0a6ea5" @@ -3562,7 +3647,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.22.1, browserslist@^4.22.2: +browserslist@^4.21.10, browserslist@^4.22.1, browserslist@^4.22.2: version "4.23.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== @@ -3572,16 +3657,6 @@ browserslist@^4.22.1, browserslist@^4.22.2: node-releases "^2.0.14" update-browserslist-db "^1.0.16" -browserslist@^4.23.0: - version "4.24.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" - integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== - dependencies: - caniuse-lite "^1.0.30001669" - electron-to-chromium "^1.5.41" - node-releases "^2.0.18" - update-browserslist-db "^1.1.1" - bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -3697,12 +3772,7 @@ camelcase@^7.0.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== -caniuse-lite@^1.0.30001605, caniuse-lite@^1.0.30001669: - version "1.0.30001677" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz#27c2e2c637e007cfa864a16f7dfe7cde66b38b5f" - integrity sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog== - -caniuse-lite@^1.0.30001629: +caniuse-lite@^1.0.30001524, caniuse-lite@^1.0.30001629: version "1.0.30001636" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz#b15f52d2bdb95fad32c2f53c0b68032b85188a78" integrity sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg== @@ -4123,6 +4193,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-env@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" + integrity sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ== + dependencies: + cross-spawn "^6.0.5" + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -4153,7 +4230,27 @@ cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.1: + version "7.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" + integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4599,11 +4696,6 @@ electron-to-chromium@^1.4.796: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.803.tgz#cf55808a5ee12e2a2778bbe8cdc941ef87c2093b" integrity sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g== -electron-to-chromium@^1.5.41: - version "1.5.50" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz#d9ba818da7b2b5ef1f3dd32bce7046feb7e93234" - integrity sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw== - emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -4801,11 +4893,6 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== -escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - escape-goat@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-4.0.0.tgz#9424820331b510b0666b98f7873fe11ac4aa8081" @@ -4849,19 +4936,18 @@ eslint-compat-utils@^0.5.1: dependencies: semver "^7.5.4" -eslint-plugin-compat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-5.0.0.tgz#d09dff02397d81c9f5b1ac740ef45b39538aa21d" - integrity sha512-29KNWyFkUbNVf6TIKVe9SVCGCtHjML3HnUg9C8LG2GsXf7miAeBOgdMc1n2B5n0sHUzg1/A4IFly7Jyf1gSbgQ== +eslint-plugin-compat@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz#eeaf80daa1afe495c88a47e9281295acae45c0aa" + integrity sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w== dependencies: - "@mdn/browser-compat-data" "^5.5.19" + "@mdn/browser-compat-data" "^5.3.13" ast-metadata-inferer "^0.8.0" - browserslist "^4.23.0" - caniuse-lite "^1.0.30001605" + browserslist "^4.21.10" + caniuse-lite "^1.0.30001524" find-up "^5.0.0" - globals "^13.24.0" lodash.memoize "^4.1.2" - semver "^7.6.0" + semver "^7.5.4" eslint-plugin-jest@^27.6.0: version "27.9.0" @@ -5596,7 +5682,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0, globals@^13.24.0: +globals@^13.19.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== @@ -5692,15 +5778,6 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== -happy-dom@^14.12.0: - version "14.12.0" - resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.12.0.tgz#40c748578c6ebfb707e6ae69179d6c541d8f63b3" - integrity sha512-dHcnlGFY2o2CdxfuYpqwSrBrpj/Kuzv4u4f3TU5yHW1GL24dKij4pv1BRjXnXc3uWo8qsCbToF9weaDsm/He8A== - dependencies: - entities "^4.5.0" - webidl-conversions "^7.0.0" - whatwg-mimetype "^3.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -7630,6 +7707,11 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -7679,11 +7761,6 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -node-releases@^2.0.18: - version "2.0.18" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" - integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== - normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7995,6 +8072,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -8156,6 +8238,15 @@ postcss@^8.4.38: picocolors "^1.0.0" source-map-js "^1.2.0" +postcss@^8.4.43: + version "8.4.47" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" + integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.0" + source-map-js "^1.2.1" + preferred-pm@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.1.3.tgz#4125ea5154603136c3b6444e5f5c94ecf90e4916" @@ -8750,6 +8841,33 @@ rollup@^4.13.0: "@rollup/rollup-win32-x64-msvc" "4.18.0" fsevents "~2.3.2" +rollup@^4.20.0: + version "4.25.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.25.0.tgz#74dff4b5c2777dfc490f9711393925da50171787" + integrity sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.25.0" + "@rollup/rollup-android-arm64" "4.25.0" + "@rollup/rollup-darwin-arm64" "4.25.0" + "@rollup/rollup-darwin-x64" "4.25.0" + "@rollup/rollup-freebsd-arm64" "4.25.0" + "@rollup/rollup-freebsd-x64" "4.25.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.25.0" + "@rollup/rollup-linux-arm-musleabihf" "4.25.0" + "@rollup/rollup-linux-arm64-gnu" "4.25.0" + "@rollup/rollup-linux-arm64-musl" "4.25.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.25.0" + "@rollup/rollup-linux-riscv64-gnu" "4.25.0" + "@rollup/rollup-linux-s390x-gnu" "4.25.0" + "@rollup/rollup-linux-x64-gnu" "4.25.0" + "@rollup/rollup-linux-x64-musl" "4.25.0" + "@rollup/rollup-win32-arm64-msvc" "4.25.0" + "@rollup/rollup-win32-ia32-msvc" "4.25.0" + "@rollup/rollup-win32-x64-msvc" "4.25.0" + fsevents "~2.3.2" + run-async@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" @@ -8868,7 +8986,7 @@ semver-match@0.1.1: dependencies: semver "^5.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.5.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -9098,6 +9216,11 @@ source-map-js@^1.0.1, source-map-js@^1.2.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@0.5.21, source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -9788,47 +9911,47 @@ tty-table@^4.1.5: wcwidth "^1.0.1" yargs "^17.7.1" -turbo-darwin-64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.2.3.tgz#f0ced75ed031091e52851cbe8bb05d21a161a22b" - integrity sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg== +turbo-darwin-64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.0.4.tgz#83c7835f8ba1f7a5473487ce73cfc8d5ad523614" + integrity sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw== -turbo-darwin-arm64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.2.3.tgz#0b4741383ab5070d8383891a65861a8869cc7202" - integrity sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA== +turbo-darwin-arm64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.0.4.tgz#046e5768e9d6b490b7108d5bef3f4a1594aca0ba" + integrity sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A== -turbo-linux-64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.2.3.tgz#2b339db50c12bc52ce99139c156d5555717a209d" - integrity sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ== +turbo-linux-64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.0.4.tgz#eab8c183a11b26ddec251d62778313a495971e4f" + integrity sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg== -turbo-linux-arm64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.2.3.tgz#a4daf6e0872a4e2652e2d05d68ad18cee5b10e94" - integrity sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ== +turbo-linux-arm64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.0.4.tgz#2dcc3f1d3e56209736b2ce3d849b80e0d7116e42" + integrity sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg== -turbo-windows-64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.2.3.tgz#d44b3385948bd0f2ef5c2d53391f142bdd467b18" - integrity sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ== +turbo-windows-64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.0.4.tgz#b2440d82892c983088ed386f9126d365594fc1a5" + integrity sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA== -turbo-windows-arm64@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.2.3.tgz#d0625ec53f467013a6f259f87f7fc4ae8670aaa4" - integrity sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw== +turbo-windows-arm64@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.0.4.tgz#e943709535baf233f5b85ed35cd95dcf86815283" + integrity sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ== -turbo@^2.0.4: - version "2.2.3" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.2.3.tgz#0f45612d62526c98c75da0682aa8c26b902b5e07" - integrity sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ== +turbo@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.0.4.tgz#4fb6f0bf3be905953825de0368203e849c91e412" + integrity sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw== optionalDependencies: - turbo-darwin-64 "2.2.3" - turbo-darwin-arm64 "2.2.3" - turbo-linux-64 "2.2.3" - turbo-linux-arm64 "2.2.3" - turbo-windows-64 "2.2.3" - turbo-windows-arm64 "2.2.3" + turbo-darwin-64 "2.0.4" + turbo-darwin-arm64 "2.0.4" + turbo-linux-64 "2.0.4" + turbo-linux-arm64 "2.0.4" + turbo-windows-64 "2.0.4" + turbo-windows-arm64 "2.0.4" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -9946,10 +10069,20 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@5.4.2, typescript@^5.0.3, typescript@^5.4.5: - version "5.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== +typescript@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" + integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== + +typescript@^4.7.3, typescript@^4.9.0, typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +typescript@^5.0.3: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -10034,14 +10167,6 @@ update-browserslist-db@^1.0.16: escalade "^3.1.2" picocolors "^1.0.1" -update-browserslist-db@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" - integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.0" - update-notifier@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-6.0.2.tgz#a6990253dfe6d5a02bd04fbb6a61543f55026b60" @@ -10150,7 +10275,7 @@ vite-node@1.6.0: picocolors "^1.0.0" vite "^5.0.0" -vite-plugin-dts@^3.8.1, vite-plugin-dts@^3.9.1: +vite-plugin-dts@^3.8.1: version "3.9.1" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz#625ad388ec3956708ccec7960550a7b0a8e8909e" integrity sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg== @@ -10188,7 +10313,7 @@ vite-plugin-zip-pack@^1.2.2: dependencies: jszip "^3.10.1" -vite@^5.0.0, "vite@^5.0.0 || ^4.1.4", vite@^5.2.8, vite@^5.3.1: +vite@^5.0.0, "vite@^5.0.0 || ^4.1.4": version "5.3.1" resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.1.tgz#bb2ca6b5fd7483249d3e86b25026e27ba8a663e6" integrity sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ== @@ -10199,6 +10324,17 @@ vite@^5.0.0, "vite@^5.0.0 || ^4.1.4", vite@^5.2.8, vite@^5.3.1: optionalDependencies: fsevents "~2.3.3" +vite@^5.2.8: + version "5.4.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18" + integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + vitefu@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" @@ -10332,11 +10468,6 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -10349,11 +10480,6 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" From 23c2a13df0327860f5e8683a2d2ee30bffac2bb7 Mon Sep 17 00:00:00 2001 From: Shay Malchi Date: Sun, 5 Jan 2025 10:29:06 +0200 Subject: [PATCH 30/34] [DEV-1270] removeAnimationCSS in text nodes (#29) Co-authored-by: Shay Malchi --- packages/rrdom-nodejs/CHANGELOG.md | 8 ++++++++ packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/CHANGELOG.md | 7 +++++++ packages/rrdom/package.json | 6 +++--- packages/rrvideo/CHANGELOG.md | 7 +++++++ packages/rrvideo/package.json | 6 +++--- packages/rrweb-player/CHANGELOG.md | 2 ++ packages/rrweb-player/package.json | 4 ++-- packages/rrweb-snapshot/CHANGELOG.md | 6 ++++++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb-snapshot/src/rebuild.ts | 2 +- packages/rrweb/CHANGELOG.md | 9 +++++++++ packages/rrweb/package.json | 8 ++++---- packages/types/CHANGELOG.md | 7 +++++++ packages/types/package.json | 4 ++-- packages/web-extension/CHANGELOG.md | 8 ++++++++ packages/web-extension/package.json | 8 ++++---- 17 files changed, 77 insertions(+), 23 deletions(-) diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index 5630aacda1..d4f43323e7 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,13 @@ # rrdom-nodejs +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.16 + - @saola.ai/rrdom@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 721ba23b9c..7d3d2f5cde 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrdom-nodejs", - "version": "2.0.15", + "version": "2.0.16", "scripts": { "dev": "vite build --watch", "build": "tsc -noEmit && vite build", @@ -55,8 +55,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "@saola.ai/rrdom": "^2.0.15", - "@saola.ai/rrweb-snapshot": "^2.0.15" + "@saola.ai/rrdom": "^2.0.16", + "@saola.ai/rrweb-snapshot": "^2.0.16" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index c48509c881..905771a101 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,12 @@ # rrdom +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 9717c043fa..a47afb2831 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrdom", - "version": "2.0.15", + "version": "2.0.16", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "type": "module", @@ -41,7 +41,7 @@ "url": "https://github.com/rrweb-io/rrweb/issues" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb-types": "^2.0.16", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", @@ -52,6 +52,6 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-snapshot": "^2.0.15" + "@saola.ai/rrweb-snapshot": "^2.0.16" } } diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md index 713de82a7f..d533b3b0f3 100644 --- a/packages/rrvideo/CHANGELOG.md +++ b/packages/rrvideo/CHANGELOG.md @@ -1,5 +1,12 @@ # rrvideo +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index 500153d54a..5f1c00ae79 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrvideo", - "version": "2.0.15", + "version": "2.0.16", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -27,13 +27,13 @@ "@types/node": "^18.15.11", "jest": "^27.5.1", "ts-jest": "^27.1.3", - "@saola.ai/rrweb-types": "^2.0.15" + "@saola.ai/rrweb-types": "^2.0.16" }, "dependencies": { "@open-tech-world/cli-progress-bar": "^2.0.2", "fs-extra": "^11.1.1", "minimist": "^1.2.5", "playwright": "^1.32.1", - "@saola.ai/rrweb-player": "^2.0.15" + "@saola.ai/rrweb-player": "^2.0.16" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 2518aab9b2..86c5cf34f3 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,7 @@ # rrweb-player +## 2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 82bf34b687..fe308f6284 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,8 +1,8 @@ { "name": "@saola.ai/rrweb-player", - "version": "2.0.15", + "version": "2.0.16", "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb-types": "^2.0.16", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index 7dae5eedc0..b70c2ef7ba 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,11 @@ # rrweb-snapshot +## 2.0.16 + +### Patch Changes + +- [DEV-1270] removeAnimationCSS in text nodes + ## 2.0.15 ### Patch Changes diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 617095b932..0a83aec488 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-snapshot", - "version": "2.0.15", + "version": "2.0.16", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index ccb6624c66..8ee34278b1 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -386,7 +386,7 @@ function buildNode( case NodeType.Text: return doc.createTextNode( n.isStyle && hackCss - ? adaptCssForReplay(n.textContent, cache) + ? adaptCssForReplay(n.textContent, cache, removeAnimationCss) : n.textContent, ); case NodeType.CDATA: diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index 6820fed9d8..29461811f3 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,14 @@ # rrweb +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.16 + - @saola.ai/rrdom@2.0.16 + - @saola.ai/rrweb-types@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 643fad75b8..cd038c15f8 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb", - "version": "2.0.15", + "version": "2.0.16", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -83,12 +83,12 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb-types": "^2.0.16", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", - "@saola.ai/rrdom": "^2.0.15", - "@saola.ai/rrweb-snapshot": "^2.0.15" + "@saola.ai/rrdom": "^2.0.16", + "@saola.ai/rrweb-snapshot": "^2.0.16" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 3980e28919..56cbc20be7 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,12 @@ # @rrweb/types +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index 2ca0a1c642..c5e4d8f004 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-types", - "version": "2.0.15", + "version": "2.0.16", "publishConfig": { "access": "public" }, @@ -52,7 +52,7 @@ "dependencies": { "@changesets/cli": "^2.27.1", "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", - "@saola.ai/rrweb-snapshot": "^2.0.15", + "@saola.ai/rrweb-snapshot": "^2.0.16", "browserslist": "^4.22.1", "concurrently": "^7.1.0", "cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 6eb3e451e3..32e00fd530 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,13 @@ # @rrweb/web-extension +## 2.0.16 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.16 + - @saola.ai/rrweb-player@2.0.16 + ## 2.0.15 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 7f8233bd67..3ee7ca5180 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@saola.ai/rrweb-web-extension", "private": true, - "version": "2.0.15", + "version": "2.0.16", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -18,7 +18,7 @@ "prepublish": "yarn build" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.15", + "@saola.ai/rrweb-types": "^2.0.16", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^4.2.1", @@ -42,7 +42,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "@saola.ai/rrweb": "^2.0.15", - "@saola.ai/rrweb-player": "^2.0.15" + "@saola.ai/rrweb": "^2.0.16", + "@saola.ai/rrweb-player": "^2.0.16" } } From 7e0b32a19a5f3280a6c52c5504d12ae69d152884 Mon Sep 17 00:00:00 2001 From: Shay Malchi Date: Mon, 6 Jan 2025 20:00:04 +0200 Subject: [PATCH 31/34] [DEV-1270] Fix remote CSS being ignored when applying the mutation (#30) Co-authored-by: Shay Malchi --- packages/rrdom-nodejs/CHANGELOG.md | 8 ++++++++ packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/CHANGELOG.md | 7 +++++++ packages/rrdom/package.json | 6 +++--- packages/rrvideo/CHANGELOG.md | 7 +++++++ packages/rrvideo/package.json | 6 +++--- packages/rrweb-player/CHANGELOG.md | 2 ++ packages/rrweb-player/package.json | 4 ++-- packages/rrweb-snapshot/CHANGELOG.md | 2 ++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 11 +++++++++++ packages/rrweb/package.json | 8 ++++---- packages/rrweb/src/replay/index.ts | 28 +++++++++++++++++----------- packages/types/CHANGELOG.md | 7 +++++++ packages/types/package.json | 4 ++-- packages/web-extension/CHANGELOG.md | 8 ++++++++ packages/web-extension/package.json | 8 ++++---- 17 files changed, 91 insertions(+), 33 deletions(-) diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index d4f43323e7..5ae11a5c0f 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,13 @@ # rrdom-nodejs +## 2.0.17 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.17 + - @saola.ai/rrdom@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 7d3d2f5cde..f82ee709d6 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrdom-nodejs", - "version": "2.0.16", + "version": "2.0.17", "scripts": { "dev": "vite build --watch", "build": "tsc -noEmit && vite build", @@ -55,8 +55,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "@saola.ai/rrdom": "^2.0.16", - "@saola.ai/rrweb-snapshot": "^2.0.16" + "@saola.ai/rrdom": "^2.0.17", + "@saola.ai/rrweb-snapshot": "^2.0.17" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index 905771a101..3461527057 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,12 @@ # rrdom +## 2.0.17 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index a47afb2831..7e278988e0 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrdom", - "version": "2.0.16", + "version": "2.0.17", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "type": "module", @@ -41,7 +41,7 @@ "url": "https://github.com/rrweb-io/rrweb/issues" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.16", + "@saola.ai/rrweb-types": "^2.0.17", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", @@ -52,6 +52,6 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-snapshot": "^2.0.16" + "@saola.ai/rrweb-snapshot": "^2.0.17" } } diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md index d533b3b0f3..0ab0e7c0ae 100644 --- a/packages/rrvideo/CHANGELOG.md +++ b/packages/rrvideo/CHANGELOG.md @@ -1,5 +1,12 @@ # rrvideo +## 2.0.17 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-player@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index 5f1c00ae79..ae02f6de29 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrvideo", - "version": "2.0.16", + "version": "2.0.17", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -27,13 +27,13 @@ "@types/node": "^18.15.11", "jest": "^27.5.1", "ts-jest": "^27.1.3", - "@saola.ai/rrweb-types": "^2.0.16" + "@saola.ai/rrweb-types": "^2.0.17" }, "dependencies": { "@open-tech-world/cli-progress-bar": "^2.0.2", "fs-extra": "^11.1.1", "minimist": "^1.2.5", "playwright": "^1.32.1", - "@saola.ai/rrweb-player": "^2.0.16" + "@saola.ai/rrweb-player": "^2.0.17" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 86c5cf34f3..886ba16712 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,7 @@ # rrweb-player +## 2.0.17 + ## 2.0.16 ## 2.0.15 diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index fe308f6284..35ed13079b 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,8 +1,8 @@ { "name": "@saola.ai/rrweb-player", - "version": "2.0.16", + "version": "2.0.17", "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.16", + "@saola.ai/rrweb-types": "^2.0.17", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index b70c2ef7ba..61cdbc257c 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,7 @@ # rrweb-snapshot +## 2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 0a83aec488..1092df6336 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-snapshot", - "version": "2.0.16", + "version": "2.0.17", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index 29461811f3..00fb88032d 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,16 @@ # rrweb +## 2.0.17 + +### Patch Changes + +- [DEV-1270] Fix remote CSS being ignored when applying the mutation + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.17 + - @saola.ai/rrdom@2.0.17 + - @saola.ai/rrweb-types@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index cd038c15f8..88db579983 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb", - "version": "2.0.16", + "version": "2.0.17", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -83,12 +83,12 @@ "vite-plugin-dts": "^3.8.1" }, "dependencies": { - "@saola.ai/rrweb-types": "^2.0.16", + "@saola.ai/rrweb-types": "^2.0.17", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", - "@saola.ai/rrdom": "^2.0.16", - "@saola.ai/rrweb-snapshot": "^2.0.16" + "@saola.ai/rrdom": "^2.0.17", + "@saola.ai/rrweb-snapshot": "^2.0.17" } } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index bb991c3783..3cf19bff80 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1798,18 +1798,24 @@ export class Replayer { const newSn = mirror.getMeta( target as Node & RRNode, ) as serializedElementNodeWithId; - Object.assign( - newSn.attributes, - mutation.attributes as attributes, + + const newNode = buildNodeWithSN( + { + ...newSn, + attributes: { + ...newSn.attributes, + ...(mutation.attributes as attributes), + }, + }, + { + doc: target.ownerDocument as Document, // can be Document or RRDocument + mirror: mirror as Mirror, + skipChild: true, + hackCss: true, + cache: this.cache, + removeAnimationCss: this.config.removeAnimationCss, + }, ); - const newNode = buildNodeWithSN(newSn, { - doc: target.ownerDocument as Document, // can be Document or RRDocument - mirror: mirror as Mirror, - skipChild: true, - hackCss: true, - cache: this.cache, - removeAnimationCss: this.config.removeAnimationCss, - }); const siblingNode = target.nextSibling; const parentNode = target.parentNode; if (newNode && parentNode) { diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 56cbc20be7..f3fbe467ea 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,12 @@ # @rrweb/types +## 2.0.17 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb-snapshot@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index c5e4d8f004..ed8ffcfac2 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@saola.ai/rrweb-types", - "version": "2.0.16", + "version": "2.0.17", "publishConfig": { "access": "public" }, @@ -52,7 +52,7 @@ "dependencies": { "@changesets/cli": "^2.27.1", "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", - "@saola.ai/rrweb-snapshot": "^2.0.16", + "@saola.ai/rrweb-snapshot": "^2.0.17", "browserslist": "^4.22.1", "concurrently": "^7.1.0", "cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 32e00fd530..72d2b834df 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,13 @@ # @rrweb/web-extension +## 2.0.17 + +### Patch Changes + +- Updated dependencies []: + - @saola.ai/rrweb@2.0.17 + - @saola.ai/rrweb-player@2.0.17 + ## 2.0.16 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 3ee7ca5180..c29b2c1a5c 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@saola.ai/rrweb-web-extension", "private": true, - "version": "2.0.16", + "version": "2.0.17", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -18,7 +18,7 @@ "prepublish": "yarn build" }, "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.16", + "@saola.ai/rrweb-types": "^2.0.17", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^4.2.1", @@ -42,7 +42,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "@saola.ai/rrweb": "^2.0.16", - "@saola.ai/rrweb-player": "^2.0.16" + "@saola.ai/rrweb": "^2.0.17", + "@saola.ai/rrweb-player": "^2.0.17" } } From 073d030d309a55783ae788f5a5b8b45af9260c14 Mon Sep 17 00:00:00 2001 From: Shay Malchi Date: Wed, 29 Jan 2025 13:25:52 +0200 Subject: [PATCH 32/34] remove web-extension, build passes --- .changeset/config.json | 1 - .changeset/pre.json | 1 - .vscode/rrweb-monorepo.code-workspace | 4 - packages/web-extension/CHANGELOG.md | 282 ----------- packages/web-extension/README.md | 206 -------- packages/web-extension/package.json | 50 -- .../web-extension/src/background/index.ts | 292 ----------- .../src/components/CircleButton.tsx | 33 -- .../src/components/SidebarWithHeader.tsx | 290 ----------- packages/web-extension/src/content/index.ts | 140 ------ packages/web-extension/src/content/inject.ts | 74 --- packages/web-extension/src/manifest.json | 74 --- packages/web-extension/src/options/App.tsx | 31 -- packages/web-extension/src/options/index.html | 9 - packages/web-extension/src/options/index.tsx | 22 - packages/web-extension/src/pages/App.tsx | 37 -- packages/web-extension/src/pages/Player.tsx | 74 --- .../web-extension/src/pages/SessionList.tsx | 457 ------------------ packages/web-extension/src/pages/index.html | 14 - packages/web-extension/src/pages/index.tsx | 22 - packages/web-extension/src/popup/App.tsx | 179 ------- packages/web-extension/src/popup/Timer.tsx | 25 - packages/web-extension/src/popup/index.tsx | 15 - packages/web-extension/src/popup/popup.html | 8 - packages/web-extension/src/public/icon128.png | Bin 9279 -> 0 bytes packages/web-extension/src/public/icon16.png | Bin 3733 -> 0 bytes packages/web-extension/src/public/icon48.png | Bin 4862 -> 0 bytes packages/web-extension/src/types.ts | 86 ---- packages/web-extension/src/utils/channel.ts | 189 -------- packages/web-extension/src/utils/index.ts | 48 -- packages/web-extension/src/utils/storage.ts | 121 ----- packages/web-extension/tsconfig.json | 27 -- packages/web-extension/vite.config.ts | 134 ----- 33 files changed, 2945 deletions(-) delete mode 100644 packages/web-extension/CHANGELOG.md delete mode 100644 packages/web-extension/README.md delete mode 100644 packages/web-extension/package.json delete mode 100644 packages/web-extension/src/background/index.ts delete mode 100644 packages/web-extension/src/components/CircleButton.tsx delete mode 100644 packages/web-extension/src/components/SidebarWithHeader.tsx delete mode 100644 packages/web-extension/src/content/index.ts delete mode 100644 packages/web-extension/src/content/inject.ts delete mode 100644 packages/web-extension/src/manifest.json delete mode 100644 packages/web-extension/src/options/App.tsx delete mode 100644 packages/web-extension/src/options/index.html delete mode 100644 packages/web-extension/src/options/index.tsx delete mode 100644 packages/web-extension/src/pages/App.tsx delete mode 100644 packages/web-extension/src/pages/Player.tsx delete mode 100644 packages/web-extension/src/pages/SessionList.tsx delete mode 100644 packages/web-extension/src/pages/index.html delete mode 100644 packages/web-extension/src/pages/index.tsx delete mode 100644 packages/web-extension/src/popup/App.tsx delete mode 100644 packages/web-extension/src/popup/Timer.tsx delete mode 100644 packages/web-extension/src/popup/index.tsx delete mode 100644 packages/web-extension/src/popup/popup.html delete mode 100644 packages/web-extension/src/public/icon128.png delete mode 100644 packages/web-extension/src/public/icon16.png delete mode 100644 packages/web-extension/src/public/icon48.png delete mode 100644 packages/web-extension/src/types.ts delete mode 100644 packages/web-extension/src/utils/channel.ts delete mode 100644 packages/web-extension/src/utils/index.ts delete mode 100644 packages/web-extension/src/utils/storage.ts delete mode 100644 packages/web-extension/tsconfig.json delete mode 100644 packages/web-extension/vite.config.ts diff --git a/.changeset/config.json b/.changeset/config.json index e51f8aa68d..80da5d9b3f 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -26,7 +26,6 @@ "@saola.ai/rrdom-nodejs", "@saola.ai/rrweb-player", "@saola.ai/rrweb-types", - "@saola.ai/rrweb-web-extension", "@saola.ai/rrvideo" ] ], diff --git a/.changeset/pre.json b/.changeset/pre.json index b058736512..658cb8991a 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -8,7 +8,6 @@ "rrweb-player": "1.0.0-alpha.4", "rrweb-snapshot": "2.0.0-alpha.4", "@rrweb/types": "2.0.0-alpha.4", - "@rrweb/web-extension": "2.0.0-alpha.4", "rrvideo": "2.0.0-alpha.6", "@rrweb/all": "2.0.0-alpha.14", "@rrweb/packer": "2.0.0-alpha.14", diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index ecb1672def..df3728cedd 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -48,10 +48,6 @@ "name": "@rrweb/packer", "path": "../packages/packer" }, - { - "name": "web-extension (package)", - "path": "../packages/web-extension" - }, { "name": "rrvideo (package)", "path": "../packages/rrvideo" }, { "name": "@rrweb/rrweb-plugin-console-record", diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md deleted file mode 100644 index 3688b293f5..0000000000 --- a/packages/web-extension/CHANGELOG.md +++ /dev/null @@ -1,282 +0,0 @@ -# @rrweb/web-extension - -## 2.0.17 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.17 - - @saola.ai/rrweb-player@2.0.17 - -## 2.0.16 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.16 - - @saola.ai/rrweb-player@2.0.16 - -## 2.0.15 - -### Patch Changes - -- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - new version 2.0.15 - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.15 - - @saola.ai/rrweb@2.0.15 - -## 2.0.14 - -### Patch Changes - -- [REVERT] Commit eb1fb6d60f4381ee87272bdceccdf6ad5297284f - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.14 - - @saola.ai/rrweb@2.0.14 - -## 2.0.13 - -### Patch Changes - -- Merge from rrweb remote upstream - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.13 - - @saola.ai/rrweb@2.0.13 -## 2.0.0-alpha.18 - -### Patch Changes - -- Updated dependencies [[`04ee6ed`](https://github.com/rrweb-io/rrweb/commit/04ee6eda57157f0e04f18f907d8f3e59ababc753), [`5fbb904`](https://github.com/rrweb-io/rrweb/commit/5fbb904edb653f3da17e6775ee438d81ef0bba83), [`5a78938`](https://github.com/rrweb-io/rrweb/commit/5a789385a341311ba327a768fe0e2f0f2f5002ee), [`53b83bb`](https://github.com/rrweb-io/rrweb/commit/53b83bb037f9cb30c93179548f436ed776f143ab)]: - - rrweb@2.0.0-alpha.18 - - rrweb-player@2.0.0-alpha.18 - -## 2.0.0-alpha.17 - -### Minor Changes - -- [#1522](https://github.com/rrweb-io/rrweb/pull/1522) [`b1f9daa`](https://github.com/rrweb-io/rrweb/commit/b1f9daaa42f007a641104613bb07f141585f9e77) Thanks [@kirankunigiri](https://github.com/kirankunigiri)! - Added session downloader for chrome extension - -### Patch Changes - -- Updated dependencies [[`40bbc25`](https://github.com/rrweb-io/rrweb/commit/40bbc25fc287badc317a53f2d3f21b1c9f2b211b), [`68076b7`](https://github.com/rrweb-io/rrweb/commit/68076b724ff19d198d4f351a05063b85e1705a8c), [`8059d96`](https://github.com/rrweb-io/rrweb/commit/8059d9695146626b102b2059a3a9b932d5f598f6), [`335639a`](https://github.com/rrweb-io/rrweb/commit/335639af9b0ce7f70eb0f38ce113d877c7325158), [`be6bf52`](https://github.com/rrweb-io/rrweb/commit/be6bf52c248c35de1b3491e3a3440ff61f876414)]: - - rrweb@2.0.0-alpha.17 - - rrweb-player@2.0.0-alpha.17 - -## 2.0.0-alpha.16 - -### Patch Changes - -- Updated dependencies [[`a2c8a1a`](https://github.com/rrweb-io/rrweb/commit/a2c8a1a37bfcf8389b280af792262c8263a979a3), [`d08624c`](https://github.com/rrweb-io/rrweb/commit/d08624cb28add386c3618a0e6607424c3f1884d8)]: - - rrweb@2.0.0-alpha.16 - - rrweb-player@2.0.0-alpha.16 - -## 2.0.12 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.12 - - @saola.ai/rrweb@2.0.12 - -## 2.0.11 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.11 - - @saola.ai/rrweb-player@2.0.11 - -## 2.0.10 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.10 - - @saola.ai/rrweb@2.0.10 - -## 2.0.9 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.9 - - @saola.ai/rrweb-player@2.0.9 - -## 2.0.8 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.8 - - @saola.ai/rrweb-player@2.0.8 - -## 2.0.7 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.7 - - @saola.ai/rrweb-player@2.0.7 - -## 2.0.6 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.6 - - @saola.ai/rrweb@2.0.6 - -## 2.0.5 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.5 - - @saola.ai/rrweb@2.0.5 - -## 2.0.4 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.4 - - @saola.ai/rrweb@2.0.4 - -## 2.0.3 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.3 - - @saola.ai/rrweb@2.0.3 - -## 2.0.2 - -### Patch Changes - -- added setDims and setDimsAndScale - -- Updated dependencies []: - - @saola.ai/rrweb-player@2.0.2 - - @saola.ai/rrweb@2.0.2 - -## 2.0.1 - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.1 - - @saola.ai/rrweb-player@2.0.1 - -## 2.0.0 - -### Major Changes - -- Saola AI - -### Patch Changes - -- Updated dependencies []: - - @saola.ai/rrweb@2.0.0 - - @saola.ai/rrweb-player@2.0.0 - -## 2.0.0-alpha.15 - -### Patch Changes - -- Updated dependencies [[`7261c43`](https://github.com/rrweb-io/rrweb/commit/7261c43f60973e88325edf832e4d0e057fbff0ae), [`4014305`](https://github.com/rrweb-io/rrweb/commit/40143059446cee5c042c007b1c2e976f36e172f5), [`609b7fa`](https://github.com/rrweb-io/rrweb/commit/609b7fac79a552f746dc880a28927dee382cd082), [`82f6fec`](https://github.com/rrweb-io/rrweb/commit/82f6fecf36413ecbc994a510144487f1de20d1d5), [`2606a2a`](https://github.com/rrweb-io/rrweb/commit/2606a2a28f2a6d897b8ae4ea3ec40ef0eeacbfaf), [`2606a2a`](https://github.com/rrweb-io/rrweb/commit/2606a2a28f2a6d897b8ae4ea3ec40ef0eeacbfaf), [`5c27b76`](https://github.com/rrweb-io/rrweb/commit/5c27b763192bda9dd91806f95df7c1cd0ab083a6), [`d38893f`](https://github.com/rrweb-io/rrweb/commit/d38893f6338facf331fd1f6e63c121120b81177d), [`d7cf8dd`](https://github.com/rrweb-io/rrweb/commit/d7cf8dd07547f6fb22ef82e341a88357c4053bd3), [`2606a2a`](https://github.com/rrweb-io/rrweb/commit/2606a2a28f2a6d897b8ae4ea3ec40ef0eeacbfaf), [`e08706a`](https://github.com/rrweb-io/rrweb/commit/e08706ae60268b6eb05c6292ef948c71bd423ce3)]: - - rrweb@2.0.0-alpha.15 - - rrweb-player@2.0.0-alpha.15 - -## 2.0.0-alpha.14 - -### Patch Changes - -- Updated dependencies [[`03b5216`](https://github.com/rrweb-io/rrweb/commit/03b5216a9403f1509b4f69d1d71ef9874277fe91), [`ae6908d`](https://github.com/rrweb-io/rrweb/commit/ae6908dcdcd7c732c1ce79eea19de5240bec1151), [`46f1b25`](https://github.com/rrweb-io/rrweb/commit/46f1b252a5919c68c68e825bd6089cc2e7d34e7c), [`cbbd1e5`](https://github.com/rrweb-io/rrweb/commit/cbbd1e55f1f7fa2eed9fa11e4152b509bdfd88f7), [`e96f668`](https://github.com/rrweb-io/rrweb/commit/e96f668c86bd0ab5dc190bb2957a170271bb2ebc)]: - - rrweb@2.0.0-alpha.14 - - rrweb-player@2.0.0-alpha.14 - -## 2.0.0-alpha.13 - -### Patch Changes - -- Updated dependencies [[`3d1877c`](https://github.com/rrweb-io/rrweb/commit/3d1877cff83d9a018630674fb6e730050ceef812), [`123a81e`](https://github.com/rrweb-io/rrweb/commit/123a81e12d072cd95d701231176d7eb2d03b3961), [`123a81e`](https://github.com/rrweb-io/rrweb/commit/123a81e12d072cd95d701231176d7eb2d03b3961), [`02f50d2`](https://github.com/rrweb-io/rrweb/commit/02f50d260cfe72209c94de1679336737f238e216)]: - - rrweb@2.0.0-alpha.13 - - rrweb-player@2.0.0-alpha.13 - -## 2.0.0-alpha.12 - -### Patch Changes - -- [#1333](https://github.com/rrweb-io/rrweb/pull/1333) [`40f484d`](https://github.com/rrweb-io/rrweb/commit/40f484d088390b480f088d1b1c1c152641cd5878) Thanks [@billyvg](https://github.com/billyvg)! - Update `vite.config.ts` to account for all potential entry types. - -- [#1360](https://github.com/rrweb-io/rrweb/pull/1360) [`0f004af`](https://github.com/rrweb-io/rrweb/commit/0f004af18dd8ba204fd80a68328fc48bf229e7f0) Thanks [@huangkairan](https://github.com/huangkairan)! - 🎈 perf(web-extension): conditional check in Player component - -- [#1307](https://github.com/rrweb-io/rrweb/pull/1307) [`8444cb2`](https://github.com/rrweb-io/rrweb/commit/8444cb2dad6ceb4bef3e32a6adba05ab65b56e52) Thanks [@huangkairan](https://github.com/huangkairan)! - 🐞 fix(web-extension): typo - -- [#1330](https://github.com/rrweb-io/rrweb/pull/1330) [`9e65dda`](https://github.com/rrweb-io/rrweb/commit/9e65dda258c9b8169a4a6486b5c018f42f6c512a) Thanks [@huangkairan](https://github.com/huangkairan)! - 🐞 fix(web-extension): beforeunload logic - -- Updated dependencies [[`af0962c`](https://github.com/rrweb-io/rrweb/commit/af0962cc6c80b693bbc622520032d17342685cf6), [`57a940a`](https://github.com/rrweb-io/rrweb/commit/57a940afac0bdd14cd82937915d53110b5311673), [`8aea5b0`](https://github.com/rrweb-io/rrweb/commit/8aea5b00a4dfe5a6f59bd2ae72bb624f45e51e81), [`9c6edfe`](https://github.com/rrweb-io/rrweb/commit/9c6edfe2261680b4e92284be69f9d183b1eca8f4), [`1e0b273`](https://github.com/rrweb-io/rrweb/commit/1e0b27382210db0168d2a79d82c13698082b0983), [`1fe39ab`](https://github.com/rrweb-io/rrweb/commit/1fe39ab0db7f5d2b04f4a4f39fb5c0cfee33a1f8), [`05478c3`](https://github.com/rrweb-io/rrweb/commit/05478c36dde03a118099783d908bb3e465e9859c), [`58c9104`](https://github.com/rrweb-io/rrweb/commit/58c9104eddc8b7994a067a97daae5684e42f892f), [`980a38c`](https://github.com/rrweb-io/rrweb/commit/980a38c816d763833fc3491f56d03c959a41122d), [`a2be77b`](https://github.com/rrweb-io/rrweb/commit/a2be77b82826c4be0e7f3c7c9f7ee50476d5f6f8), [`a7c33f2`](https://github.com/rrweb-io/rrweb/commit/a7c33f2093c4d92faf7ae25e8bb0e088d122c13b), [`314a8dd`](https://github.com/rrweb-io/rrweb/commit/314a8dde5a13095873b89d07bac7c949918bf817), [`7c0dc9d`](https://github.com/rrweb-io/rrweb/commit/7c0dc9dfe1564c9d6624557c5b394e7844955882), [`07ac5c9`](https://github.com/rrweb-io/rrweb/commit/07ac5c9e1371824ec3ffb705f9250bbe10f4b73e)]: - - rrweb@2.0.0-alpha.12 - - rrweb-player@2.0.0-alpha.12 - -## 2.0.0-alpha.11 - -### Patch Changes - -- Updated dependencies [[`11f6567`](https://github.com/rrweb-io/rrweb/commit/11f6567fd81ef9ed0f954a7b6d5e39653f56004f), [`efdc167`](https://github.com/rrweb-io/rrweb/commit/efdc167ca6c039d04af83612e3d92498bb9b41a7)]: - - rrweb@2.0.0-alpha.11 - - rrweb-player@2.0.0-alpha.11 - -## 2.0.0-alpha.10 - -### Patch Changes - -- Updated dependencies [[`7103625`](https://github.com/rrweb-io/rrweb/commit/7103625b4683cbd75732ee03973e38f573847b1c), [`d872d28`](https://github.com/rrweb-io/rrweb/commit/d872d2809e3ec8d6ff5d3d5f43bc81aff70e7548), [`36da39d`](https://github.com/rrweb-io/rrweb/commit/36da39db366a9f80c28549771ed331090a1c6647), [`bbbfa22`](https://github.com/rrweb-io/rrweb/commit/bbbfa226fc5882a01ecc1607b713f0caf797775e), [`d0fbe23`](https://github.com/rrweb-io/rrweb/commit/d0fbe23c632021410a6dd45f9028a9a012467261), [`a3de582`](https://github.com/rrweb-io/rrweb/commit/a3de582e9c32be9e0ccd84bb7df756af6b0594f7)]: - - rrweb@2.0.0-alpha.10 - - rrweb-player@2.0.0-alpha.10 - -## 2.0.0-alpha.9 - -### Patch Changes - -- Updated dependencies [[`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`a1ec9a2`](https://github.com/rrweb-io/rrweb/commit/a1ec9a273e6634eec67098fdd880ee681648fbbd), [`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe), [`ebcbe8b`](https://github.com/rrweb-io/rrweb/commit/ebcbe8b0d746a0a4c07d3530387f920900f35215), [`a01a12e`](https://github.com/rrweb-io/rrweb/commit/a01a12ef6769f26aa922ccd6ac76499f0837f0c2)]: - - rrweb@2.0.0-alpha.9 - - rrweb-player@2.0.0-alpha.9 - -## 2.0.0-alpha.8 - -### Patch Changes - -- Updated dependencies [[`b5e30cf`](https://github.com/rrweb-io/rrweb/commit/b5e30cf6cc7f5335d674ef1917a92bdf2895fe9e), [`979d2b1`](https://github.com/rrweb-io/rrweb/commit/979d2b1847a3d05e2731722952e4d6bd8be54f40), [`bc84246`](https://github.com/rrweb-io/rrweb/commit/bc84246f78849a80dbb8fe9b4e76117afcc5c3f7), [`aa79db7`](https://github.com/rrweb-io/rrweb/commit/aa79db7568578ea3a413292450cd64f07481e5dd)]: - - rrweb-player@2.0.0-alpha.8 - - rrweb@2.0.0-alpha.8 - -## 2.0.0-alpha.7 - -### Patch Changes - -- Updated dependencies [[`e0f862b`](https://github.com/rrweb-io/rrweb/commit/e0f862bac7dbaa9cfd778f5ef0f5f3fd8cbe6def), [`267e990`](https://github.com/rrweb-io/rrweb/commit/267e990dc0e45a5acaaa3ee89db7ae9171520d54), [`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`a225d8e`](https://github.com/rrweb-io/rrweb/commit/a225d8e1412a69a761c22eb45565fff0b0ce5c11), [`a82a3b4`](https://github.com/rrweb-io/rrweb/commit/a82a3b42b125aaaea607410b49f012933466c523), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`4cb4d0e`](https://github.com/rrweb-io/rrweb/commit/4cb4d0e95a540a366bdec157fe78d9f099514818)]: - - rrweb@2.0.0-alpha.7 - - rrweb-player@2.0.0-alpha.7 - -## 2.0.0-alpha.6 - -### Patch Changes - -- Updated dependencies [[`e65465e`](https://github.com/rrweb-io/rrweb/commit/e65465e808178a80a4ba84970f02162ba812955e), [`f27e545`](https://github.com/rrweb-io/rrweb/commit/f27e545e1871ed2c1753d37543f556e8ddc406b4), [`f6f07e9`](https://github.com/rrweb-io/rrweb/commit/f6f07e953376634a4caf28ff8cbfed5a017c4347), [`3416c3a`](https://github.com/rrweb-io/rrweb/commit/3416c3a769e2bd2ddfbb88f5c4ff139871c567be), [`8e47ca1`](https://github.com/rrweb-io/rrweb/commit/8e47ca1021ebb4fc036b37623ef10abf7976d6dd), [`aaabdbd`](https://github.com/rrweb-io/rrweb/commit/aaabdbdff5df2abd1a294c40ed89e74bf8b2ec7c), [`5e6c132`](https://github.com/rrweb-io/rrweb/commit/5e6c132a4d0e5f5524b2201d6a73dae62b4a0877)]: - - rrweb@2.0.0-alpha.6 - - rrweb-player@2.0.0-alpha.6 - -## 2.0.0-alpha.5 - -### Patch Changes - -- [#1044](https://github.com/rrweb-io/rrweb/pull/1044) [`282c8fa`](https://github.com/rrweb-io/rrweb/commit/282c8fa415318e56e8b63fa30ce3f173813b39c3) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add rrweb browser extension - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: - - rrweb@2.0.0-alpha.5 - - rrweb-player@2.0.0-alpha.5 diff --git a/packages/web-extension/README.md b/packages/web-extension/README.md deleted file mode 100644 index b112aa98da..0000000000 --- a/packages/web-extension/README.md +++ /dev/null @@ -1,206 +0,0 @@ -

- -

- -# rrweb extension - -The package web-extension provides a browser extension for recording and replaying web pages. - -## Installation - -``` -yarn install -``` - -## Build - -```bash -# build for chrome -yarn build:chrome - -# build for firefox -yarn build:firefox -``` - -## Development - -```bash -# start a development chrome browser -yarn dev:chrome -# start a development firefox browser -yarn dev:firefox -``` - -## Sponsors - -[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site. - -### Gold Sponsors 🥇 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Silver Sponsors 🥈 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Bronze Sponsors 🥉 - -
- -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor -sponsor - -
- -### Backers - - - -## Core Team Members - - - - - - - - -
- - -
Yuyz0112 -

-
-
- - -
Yun Feng -

-
-
- - -
eoghanmurray -

-
-
- - -
Juice10 -
open for rrweb consulting -
-
- -## Who's using rrweb? - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - Smart screen recording for SaaS - -
- - The first ever UX automation tool - - - - Remote Access & Co-Browsing - - - - The open source, fullstack Monitoring Platform. - - - - Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions. - -
- - Intercept, Modify, Record & Replay HTTP Requests. - - - - In-app bug reporting & customer feedback platform. - - - - Self-hosted website analytics with heatmaps and session recordings. - - - - Interactive product demos for small marketing teams - -
diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json deleted file mode 100644 index 77dd96d6c4..0000000000 --- a/packages/web-extension/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@saola.ai/rrweb-web-extension", - "private": true, - "version": "2.0.18", - "description": "The web extension of rrweb which helps to run rrweb on any website out of box", - "author": "rrweb-io", - "license": "MIT", - "type": "module", - "scripts": { - "dev:chrome": "cross-env TARGET_BROWSER=chrome vite dev", - "dev:firefox": "cross-env TARGET_BROWSER=firefox vite dev", - "build:chrome": "cross-env TARGET_BROWSER=chrome vite build", - "build:firefox": "cross-env TARGET_BROWSER=firefox vite build", - "pack:chrome": "cross-env TARGET_BROWSER=chrome ZIP=true vite build", - "pack:firefox": "cross-env TARGET_BROWSER=firefox ZIP=true vite build", - "check-types": "tsc -noEmit", - "build": "npm run pack:chrome && npm run pack:firefox", - "prepublish": "yarn build" - }, - "devDependencies": { - "@saola.ai/rrweb-types": "^2.0.18", - "@types/chrome": "^0.0.287", - "@types/react-dom": "^18.0.6", - "@types/semver": "^7.5.8", - "@types/webextension-polyfill": "^0.9.1", - "@vitejs/plugin-react": "^4.2.1", - "semver": "^7.6.3", - "type-fest": "^2.19.0", - "vite": "^5.3.1", - "vite-plugin-web-extension": "^4.1.3", - "vite-plugin-zip-pack": "^1.2.2", - "webextension-polyfill": "^0.10.0" - }, - "dependencies": { - "@chakra-ui/react": "^2.3.4", - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@tanstack/react-table": "^8.5.22", - "framer-motion": "^7.3.6", - "idb": "^7.1.1", - "mitt": "^3.0.0", - "nanoid": "^4.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-icons": "^4.4.0", - "react-router-dom": "^6.4.1", - "@saola.ai/rrweb": "^2.0.18", - "@saola.ai/rrweb-player": "^2.0.18" - } -} diff --git a/packages/web-extension/src/background/index.ts b/packages/web-extension/src/background/index.ts deleted file mode 100644 index d0c7ae12d6..0000000000 --- a/packages/web-extension/src/background/index.ts +++ /dev/null @@ -1,292 +0,0 @@ -import Browser from 'webextension-polyfill'; -import { nanoid } from 'nanoid'; -import type { eventWithTime } from '@saola.ai/rrweb-types'; -import Channel from '~/utils/channel'; -import { - EventName, - LocalDataKey, - MessageName, - RecorderStatus, - ServiceName, - SyncDataKey, -} from '~/types'; -import type { - LocalData, - RecordStartedMessage, - RecordStoppedMessage, - Session, - Settings, - SyncData, -} from '~/types'; -import { isFirefox } from '~/utils'; -import { addSession } from '~/utils/storage'; - -void (async () => { - // assign default value to settings of this extension - const result = - ((await Browser.storage.sync.get(SyncDataKey.settings)) as SyncData) || - undefined; - const defaultSettings: Settings = {}; - let settings = defaultSettings; - if (result && result.settings) { - setDefaultSettings(result.settings, defaultSettings); - settings = result.settings; - } - await Browser.storage.sync.set({ - settings, - } as SyncData); - - const events: eventWithTime[] = []; - const channel = new Channel(); - let recorderStatus: LocalData[LocalDataKey.recorderStatus] = { - status: RecorderStatus.IDLE, - activeTabId: -1, - }; - // Reset recorder status when the extension is reloaded. - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - - channel.on(EventName.StartButtonClicked, async () => { - if (recorderStatus.status !== RecorderStatus.IDLE) return; - recorderStatus = { - status: RecorderStatus.IDLE, - activeTabId: -1, - }; - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - - events.length = 0; // clear events before recording - const tabId = await channel.getCurrentTabId(); - if (tabId === -1) return; - - const res = (await channel - .requestToTab(tabId, ServiceName.StartRecord, {}) - .catch(async (error: Error) => { - recorderStatus.errorMessage = error.message; - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - })) as RecordStartedMessage; - if (!res) return; - Object.assign(recorderStatus, { - status: RecorderStatus.RECORDING, - activeTabId: tabId, - startTimestamp: res.startTimestamp, - }); - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - }); - - channel.on(EventName.StopButtonClicked, async () => { - if (recorderStatus.status === RecorderStatus.IDLE) return; - - if (recorderStatus.status === RecorderStatus.RECORDING) - (await channel - .requestToTab(recorderStatus.activeTabId, ServiceName.StopRecord, {}) - .catch(() => ({ - message: MessageName.RecordStopped, - endTimestamp: Date.now(), - }))) as RecordStoppedMessage; - recorderStatus = { - status: RecorderStatus.IDLE, - activeTabId: -1, - }; - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - const title = - (await Browser.tabs - .query({ active: true, currentWindow: true }) - .then((tabs) => tabs[0]?.title) - .catch(() => { - // ignore error - })) ?? 'new session'; - const newSession = generateSession(title); - await addSession(newSession, events).catch((e) => { - recorderStatus.errorMessage = (e as { message: string }).message; - void Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - }); - channel.emit(EventName.SessionUpdated, { - session: newSession, - }); - events.length = 0; - }); - - async function pauseRecording(newStatus: RecorderStatus) { - if ( - recorderStatus.status !== RecorderStatus.RECORDING || - recorderStatus.activeTabId === -1 - ) - return; - - const stopResponse = (await channel - .requestToTab(recorderStatus.activeTabId, ServiceName.StopRecord, {}) - .catch(() => { - // ignore error - })) as RecordStoppedMessage | undefined; - Object.assign(recorderStatus, { - status: newStatus, - activeTabId: -1, - pausedTimestamp: stopResponse?.endTimestamp, - }); - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - } - channel.on(EventName.PauseButtonClicked, async () => { - if (recorderStatus.status !== RecorderStatus.RECORDING) return; - await pauseRecording(RecorderStatus.PAUSED); - }); - - async function resumeRecording(newTabId: number) { - if ( - ![RecorderStatus.PAUSED, RecorderStatus.PausedSwitch].includes( - recorderStatus.status, - ) - ) - return; - const { startTimestamp, pausedTimestamp } = recorderStatus; - // On Firefox, the new tab is not communicable immediately after it is created. - if (isFirefox()) await new Promise((r) => setTimeout(r, 50)); - const pausedTime = pausedTimestamp ? Date.now() - pausedTimestamp : 0; - // Decrease the time spent in the pause state and make them look like a continuous recording. - events.forEach((event) => { - event.timestamp += pausedTime; - }); - const startResponse = (await channel - .requestToTab(newTabId, ServiceName.StartRecord, {}) - .catch((e: { message: string }) => { - recorderStatus.errorMessage = e.message; - void Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - })) as RecordStartedMessage | undefined; - if (!startResponse) { - // Restore the events data when the recording fails to start. - events.forEach((event) => { - event.timestamp -= pausedTime; - }); - return; - } - recorderStatus = { - status: RecorderStatus.RECORDING, - activeTabId: newTabId, - startTimestamp: (startTimestamp || Date.now()) + pausedTime, - }; - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - } - channel.on(EventName.ResumeButtonClicked, async () => { - if (recorderStatus.status !== RecorderStatus.PAUSED) return; - recorderStatus.errorMessage = undefined; - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - const tabId = await channel.getCurrentTabId(); - await resumeRecording(tabId); - }); - - channel.on(EventName.ContentScriptEmitEvent, (data) => { - events.push(data as eventWithTime); - }); - - // When tab is changed during the recording process, pause recording in the old tab and start a new one in the new tab. - Browser.tabs.onActivated.addListener((activeInfo) => { - void (async () => { - if ( - recorderStatus.status !== RecorderStatus.RECORDING && - recorderStatus.status !== RecorderStatus.PausedSwitch - ) - return; - if (activeInfo.tabId === recorderStatus.activeTabId) return; - if (recorderStatus.status === RecorderStatus.RECORDING) - await pauseRecording(RecorderStatus.PausedSwitch); - if (recorderStatus.status === RecorderStatus.PausedSwitch) - await resumeRecording(activeInfo.tabId); - })(); - return; - }); - - // If the recording can't start on an invalid tab, resume it when the tab content is updated. - Browser.tabs.onUpdated.addListener(function (tabId, info) { - if (info.status !== 'complete') return; - if ( - recorderStatus.status !== RecorderStatus.PausedSwitch || - recorderStatus.activeTabId === tabId - ) - return; - void resumeRecording(tabId); - }); - - /** - * When the current tab is closed, and there's no other tab to resume recording, make sure the recording status is updated to SwitchPaused. - */ - Browser.tabs.onRemoved.addListener((tabId) => { - void (async () => { - if ( - recorderStatus.activeTabId !== tabId || - recorderStatus.status !== RecorderStatus.RECORDING - ) - return; - // Update the recording status to make it resumable after users switch to other tabs. - Object.assign(recorderStatus, { - status: RecorderStatus.PausedSwitch, - activeTabId: -1, - pausedTimestamp: Date.now(), - }); - - await Browser.storage.local.set({ - [LocalDataKey.recorderStatus]: recorderStatus, - }); - })(); - }); -})(); - -/** - * Update existed settings with new settings. - * Set new setting values if these properties don't exist in older versions. - */ -function setDefaultSettings( - existedSettings: Record, - newSettings: Record, -) { - for (const i in newSettings) { - // settings[i] contains key-value settings - if ( - typeof newSettings[i] === 'object' && - !(newSettings[i] instanceof Array) && - Object.keys(newSettings[i] as Record).length > 0 - ) { - if (existedSettings[i]) { - setDefaultSettings( - existedSettings[i] as Record, - newSettings[i] as Record, - ); - } else { - // settings[i] contains several setting items but these have not been set before - existedSettings[i] = newSettings[i]; - } - } else if (existedSettings[i] === undefined) { - // settings[i] is a single setting item and it has not been set before - existedSettings[i] = newSettings[i]; - } - } -} - -function generateSession(title: string) { - const newSession: Session = { - id: nanoid(), - name: title, - tags: [], - createTimestamp: Date.now(), - modifyTimestamp: Date.now(), - recorderVersion: Browser.runtime.getManifest().version_name || 'unknown', - }; - return newSession; -} diff --git a/packages/web-extension/src/components/CircleButton.tsx b/packages/web-extension/src/components/CircleButton.tsx deleted file mode 100644 index f114a1cb66..0000000000 --- a/packages/web-extension/src/components/CircleButton.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Button, type ButtonProps } from '@chakra-ui/react'; - -interface CircleButtonProps extends ButtonProps { - diameter: number; - onClick?: () => void; - children?: React.ReactNode; - title?: string; -} - -export function CircleButton({ - diameter, - onClick, - children, - title, - ...rest -}: CircleButtonProps) { - return ( - - ); -} diff --git a/packages/web-extension/src/components/SidebarWithHeader.tsx b/packages/web-extension/src/components/SidebarWithHeader.tsx deleted file mode 100644 index 7895efe613..0000000000 --- a/packages/web-extension/src/components/SidebarWithHeader.tsx +++ /dev/null @@ -1,290 +0,0 @@ -import type { ReactNode } from 'react'; -import { - IconButton, - Box, - CloseButton, - Flex, - HStack, - Icon, - Image, - useColorModeValue, - Link, - Drawer, - DrawerContent, - useDisclosure, - type BoxProps, - type FlexProps, - Heading, - Stack, - Text, - Popover, - PopoverTrigger, - PopoverContent, -} from '@chakra-ui/react'; -import { FiChevronRight, FiMenu } from 'react-icons/fi'; -import type { IconType } from 'react-icons'; -import Browser from 'webextension-polyfill'; - -export interface SideBarItem { - label: string; - icon: IconType; - href: string; -} - -export interface HeadBarItem { - label: string; - subLabel?: string; - children?: Array; - href?: string; -} - -export default function SidebarWithHeader({ - children, - title, - headBarItems, - sideBarItems, -}: { - title?: string; - sideBarItems: SideBarItem[]; - headBarItems: SideBarItem[]; - children: ReactNode; -}) { - const { isOpen, onOpen, onClose } = useDisclosure(); - return ( - - onClose} - display={{ base: 'none', md: 'block' }} - title={title} - /> - - - - - - - - - - - {children} - - ); -} - -interface SidebarProps extends BoxProps { - onClose: () => void; - title?: string; - sideBarItems: SideBarItem[]; -} - -const SidebarContent = ({ - onClose, - sideBarItems, - title, - ...rest -}: SidebarProps) => { - return ( - - - - RRWeb Logo - - {title && ( - - {title} - - )} - - - {sideBarItems.map((link) => ( - - {link.label} - - ))} - - ); -}; - -interface NavItemProps extends FlexProps { - icon: IconType; - href: string; - children: string; -} -const NavItem = ({ icon, href, children, ...rest }: NavItemProps) => { - return ( - - - <> - {icon && } - {children} - - - - ); -}; - -interface MobileProps extends FlexProps { - onOpen: () => void; -} -const MobileNav = ({ onOpen, ...rest }: MobileProps) => { - return ( - - } - /> - - - {rest.children && rest.children} - - - ); -}; - -const DesktopNav = ({ headBarItems }: { headBarItems: HeadBarItem[] }) => { - const linkColor = useColorModeValue('gray.600', 'gray.200'); - const linkHoverColor = useColorModeValue('gray.800', 'white'); - const popoverContentBgColor = useColorModeValue('white', 'gray.800'); - - return ( - - {headBarItems.map((navItem) => ( - - - - - {navItem.label} - - - - {navItem.children && ( - - - {navItem.children.map((child) => ( - - ))} - - - )} - - - ))} - - ); -}; - -const DesktopSubNav = ({ label, href, subLabel }: HeadBarItem) => { - return ( - - - - - {label} - - {subLabel} - - - - - - - ); -}; diff --git a/packages/web-extension/src/content/index.ts b/packages/web-extension/src/content/index.ts deleted file mode 100644 index baf1c8de8a..0000000000 --- a/packages/web-extension/src/content/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -import Browser from 'webextension-polyfill'; -import { - type LocalData, - LocalDataKey, - RecorderStatus, - ServiceName, - type RecordStartedMessage, - type RecordStoppedMessage, - MessageName, - type EmitEventMessage, - EventName, -} from '~/types'; -import Channel from '~/utils/channel'; -import { isInCrossOriginIFrame } from '~/utils'; - -const channel = new Channel(); - -void (() => { - window.addEventListener( - 'message', - ( - event: MessageEvent<{ - message: MessageName; - }>, - ) => { - if (event.source !== window) return; - if (event.data.message === MessageName.RecordScriptReady) - window.postMessage( - { - message: MessageName.StartRecord, - config: { - recordCrossOriginIframes: true, - }, - }, - location.origin, - ); - }, - ); - if (isInCrossOriginIFrame()) { - void initCrossOriginIframe(); - } else if (window === window.top) { - void initMainPage(); - } -})(); - -async function initMainPage() { - let startResponseCb: ((response: RecordStartedMessage) => void) | undefined = - undefined; - channel.provide(ServiceName.StartRecord, async () => { - startRecord(); - return new Promise((resolve) => { - startResponseCb = (response) => { - resolve(response); - }; - }); - }); - let stopResponseCb: ((response: RecordStoppedMessage) => void) | undefined = - undefined; - channel.provide(ServiceName.StopRecord, () => { - window.postMessage({ message: MessageName.StopRecord }); - return new Promise((resolve) => { - stopResponseCb = (response: RecordStoppedMessage) => { - stopResponseCb = undefined; - resolve(response); - }; - }); - }); - - window.addEventListener( - 'message', - ( - event: MessageEvent< - | RecordStartedMessage - | RecordStoppedMessage - | EmitEventMessage - | { - message: MessageName; - } - >, - ) => { - if (event.source !== window) return; - else if ( - event.data.message === MessageName.RecordStarted && - startResponseCb - ) - startResponseCb(event.data as RecordStartedMessage); - else if ( - event.data.message === MessageName.RecordStopped && - stopResponseCb - ) { - // On firefox, the event.data is immutable, so we need to clone it to avoid errors. - const data = { ...(event.data as RecordStoppedMessage) }; - stopResponseCb(data); - } else if (event.data.message === MessageName.EmitEvent) - channel.emit( - EventName.ContentScriptEmitEvent, - (event.data as EmitEventMessage).event, - ); - }, - ); - - const localData = (await Browser.storage.local.get()) as LocalData; - if ( - localData?.[LocalDataKey.recorderStatus]?.status === - RecorderStatus.RECORDING - ) { - startRecord(); - } -} - -async function initCrossOriginIframe() { - Browser.storage.local.onChanged.addListener((change) => { - if (change[LocalDataKey.recorderStatus]) { - const statusChange = change[LocalDataKey.recorderStatus]; - const newStatus = - statusChange.newValue as LocalData[LocalDataKey.recorderStatus]; - if (newStatus.status === RecorderStatus.RECORDING) startRecord(); - else - window.postMessage( - { message: MessageName.StopRecord }, - location.origin, - ); - } - }); - const localData = (await Browser.storage.local.get()) as LocalData; - if ( - localData?.[LocalDataKey.recorderStatus]?.status === - RecorderStatus.RECORDING - ) - startRecord(); -} - -function startRecord() { - const scriptEl = document.createElement('script'); - scriptEl.src = Browser.runtime.getURL('content/inject.js'); - document.documentElement.appendChild(scriptEl); - scriptEl.onload = () => { - document.documentElement.removeChild(scriptEl); - }; -} diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts deleted file mode 100644 index c6a9524bf0..0000000000 --- a/packages/web-extension/src/content/inject.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { record } from '@saola.ai/rrweb'; -import type { recordOptions } from '@saola.ai/rrweb'; -import type { eventWithTime } from '@saola.ai/rrweb-types'; -import { MessageName, type RecordStartedMessage } from '~/types'; -import { isInCrossOriginIFrame } from '~/utils'; - -/** - * This script is injected into both main page and cross-origin IFrames through - diff --git a/packages/web-extension/src/options/index.tsx b/packages/web-extension/src/options/index.tsx deleted file mode 100644 index 0114523911..0000000000 --- a/packages/web-extension/src/options/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { ChakraProvider } from '@chakra-ui/react'; -import * as ReactDOM from 'react-dom/client'; -import { createHashRouter, RouterProvider } from 'react-router-dom'; -import App from './App'; - -const rootElement = document.getElementById('root'); -const router = createHashRouter([ - { - path: '/*', - element: , - }, -]); - -rootElement && - ReactDOM.createRoot(rootElement).render( - - - - - , - ); diff --git a/packages/web-extension/src/pages/App.tsx b/packages/web-extension/src/pages/App.tsx deleted file mode 100644 index 9a352a95a4..0000000000 --- a/packages/web-extension/src/pages/App.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Route, Routes } from 'react-router-dom'; -import SidebarWithHeader from '~/components/SidebarWithHeader'; -import { SessionList } from './SessionList'; -import { FiList, FiSettings } from 'react-icons/fi'; -import Player from './Player'; - -export default function App() { - return ( - - - } /> - } /> - - - ); -} diff --git a/packages/web-extension/src/pages/Player.tsx b/packages/web-extension/src/pages/Player.tsx deleted file mode 100644 index f0f71b1c14..0000000000 --- a/packages/web-extension/src/pages/Player.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/// -import { useRef, useEffect, useState } from 'react'; -import { useParams } from 'react-router-dom'; -import Replayer from '@saola.ai/rrweb-player'; -import { - Box, - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - Center, -} from '@chakra-ui/react'; -import { getEvents, getSession } from '~/utils/storage'; - -export default function Player() { - const playerElRef = useRef(null); - const playerRef = useRef(null); - const { sessionId } = useParams(); - const [sessionName, setSessionName] = useState(''); - - useEffect(() => { - if (!sessionId) return; - getSession(sessionId) - .then((session) => { - setSessionName(session.name); - }) - .catch((err) => { - console.error(err); - }); - getEvents(sessionId) - .then((events) => { - if (!playerElRef.current) return; - if (playerRef.current) return; - - const manifest = chrome.runtime.getManifest(); - const rrwebPlayerVersion = manifest.version_name || manifest.version; - const linkEl = document.createElement('link'); - linkEl.href = `https://cdn.jsdelivr.net/npm/rrweb-player@${rrwebPlayerVersion}/dist/style.min.css`; - linkEl.rel = 'stylesheet'; - document.head.appendChild(linkEl); - playerRef.current = new Replayer({ - target: playerElRef.current as HTMLElement, - props: { - events, - autoPlay: true, - }, - }); - }) - .catch((err) => { - console.error(err); - }); - return () => { - // eslint-disable-next-line - playerRef.current?.pause(); - // eslint-disable-next-line - playerRef.current?.$destroy(); - }; - }, [sessionId]); - - return ( - <> - - - Sessions - - - {sessionName} - - -
- -
- - ); -} diff --git a/packages/web-extension/src/pages/SessionList.tsx b/packages/web-extension/src/pages/SessionList.tsx deleted file mode 100644 index 03c9c552ed..0000000000 --- a/packages/web-extension/src/pages/SessionList.tsx +++ /dev/null @@ -1,457 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; -import { nanoid } from 'nanoid'; -import { - Box, - Button, - chakra, - Checkbox, - Divider, - Editable, - EditableInput, - EditablePreview, - Flex, - IconButton, - Input, - Select, - Spacer, - Table, - TableContainer, - Tbody, - Td, - Text, - Th, - Thead, - Tr, - useEditableControls, - useToast, -} from '@chakra-ui/react'; -import { - createColumnHelper, - useReactTable, - flexRender, - getCoreRowModel, - type SortingState, - getSortedRowModel, - type PaginationState, -} from '@tanstack/react-table'; -import { VscTriangleDown, VscTriangleUp } from 'react-icons/vsc'; -import { FiEdit3 as EditIcon } from 'react-icons/fi'; -import { useNavigate } from 'react-router-dom'; -import type { eventWithTime } from 'rrweb'; -import { type Session, EventName } from '~/types'; -import Channel from '~/utils/channel'; -import { - deleteSessions, - getAllSessions, - downloadSessions, - addSession, - updateSession, -} from '~/utils/storage'; -import { - FiChevronLeft, - FiChevronRight, - FiChevronsLeft, - FiChevronsRight, -} from 'react-icons/fi'; - -const columnHelper = createColumnHelper(); -const channel = new Channel(); - -export function SessionList() { - const navigate = useNavigate(); - const toast = useToast(); - const fileInputRef = useRef(null); - const [sessions, setSessions] = useState([]); - const [sorting, setSorting] = useState([ - { - id: 'createTimestamp', - desc: true, - }, - ]); - const [rowSelection, setRowSelection] = useState({}); - - const [{ pageIndex, pageSize }, setPagination] = useState({ - pageIndex: 0, - pageSize: 10, - }); - - const fetchDataOptions = { - pageIndex, - pageSize, - }; - - const fetchData = (options: { pageIndex: number; pageSize: number }) => { - return { - rows: sessions.slice( - options.pageIndex * options.pageSize, - (options.pageIndex + 1) * options.pageSize, - ), - pageCount: Math.ceil(sessions.length / options.pageSize), - }; - }; - const pagination = useMemo( - () => ({ - pageIndex, - pageSize, - }), - [pageIndex, pageSize], - ); - - const columns = useMemo( - () => [ - columnHelper.display({ - id: 'select', - header: ({ table }) => ( - - ), - cell: ({ row }) => ( - - ), - }), - columnHelper.accessor((row) => row.name, { - cell: (info) => { - const [isHovered, setIsHovered] = useState(false); - function EditableControls() { - const { isEditing, getEditButtonProps } = useEditableControls(); - return ( - isHovered && - !isEditing && ( - e.stopPropagation()} - > - } - variant="ghost" - {...getEditButtonProps()} - /> - - ) - ); - } - - return ( - setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - alignItems="center" - position="relative" - > - { - const newSession = { ...info.row.original, name: nextValue }; - setSessions( - sessions.map((s) => - s.id === newSession.id ? newSession : s, - ), - ); - void updateSession(newSession); - }} - > - - - e.stopPropagation()} /> - - - ); - }, - header: 'Name', - }), - columnHelper.accessor((row) => row.createTimestamp, { - id: 'createTimestamp', - cell: (info) => new Date(info.getValue()).toLocaleString(), - header: 'Created Time', - sortDescFirst: true, - }), - columnHelper.accessor((row) => row.recorderVersion, { - cell: (info) => info.getValue(), - header: 'RRWEB Version', - }), - ], - [sessions], - ); - const table = useReactTable({ - columns, - data: fetchData(fetchDataOptions).rows, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - onSortingChange: setSorting, - getSortedRowModel: getSortedRowModel(), - state: { - pagination, - sorting, - rowSelection, - }, - manualPagination: true, - pageCount: fetchData(fetchDataOptions).pageCount, - }); - - const updateSessions = async () => { - const sessions = await getAllSessions(); - setSessions(sessions); - }; - - useEffect(() => { - void updateSessions(); - channel.on(EventName.SessionUpdated, () => { - void updateSessions(); - }); - }, []); - - const handleFileUpload = (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = async (e) => { - try { - const content = e.target?.result as string; - const data = JSON.parse(content) as { - session: Session; - events: eventWithTime[]; - }; - const id = nanoid(); - data.session.id = id; - await addSession(data.session, data.events); - toast({ - title: 'Session imported', - description: 'The session was successfully imported.', - status: 'success', - duration: 3000, - isClosable: true, - }); - await updateSessions(); - } catch (error) { - console.error('Error uploading file:', error); - toast({ - title: 'Error importing session', - description: (error as Error).message, - status: 'error', - duration: 3000, - isClosable: true, - }); - } - }; - reader.readAsText(file); - }; - - return ( - <> - - - - - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - const meta = header.column.columnDef.meta as - | { - isNumeric: boolean; - } - | undefined; - return ( - - ); - })} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell, index) => { - const meta = cell.column.columnDef.meta as - | { - isNumeric: boolean; - } - | undefined; - return ( - - ); - })} - - ))} - -
- - {flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - {{ - asc: ( - - ), - desc: ( - - ), - }[header.column.getIsSorted() as string] ?? null} - - -
{ - if (index !== 0) - navigate(`/session/${row.original.id}`); - }} - > - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} -
-
- - - - table.setPageIndex(0)} - disabled={!table.getCanPreviousPage()} - > - - - table.previousPage()} - disabled={!table.getCanPreviousPage()} - > - - - - - Page - - {`${ - table.getState().pagination.pageIndex + 1 - } of ${table.getPageCount()}`} - - - - - Go to page: - { - const page = e.target.value ? Number(e.target.value) - 1 : 0; - table.setPageIndex(page); - }} - /> - - - table.nextPage()} - disabled={!table.getCanNextPage()} - > - - - table.setPageIndex(table.getPageCount() - 1)} - disabled={!table.getCanNextPage()} - > - - - - - - - - {Object.keys(rowSelection).length > 0 && ( - - - - - )} - - - - ); -} diff --git a/packages/web-extension/src/pages/index.html b/packages/web-extension/src/pages/index.html deleted file mode 100644 index 3450454799..0000000000 --- a/packages/web-extension/src/pages/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - rrweb - - - -
- - - - \ No newline at end of file diff --git a/packages/web-extension/src/pages/index.tsx b/packages/web-extension/src/pages/index.tsx deleted file mode 100644 index 0114523911..0000000000 --- a/packages/web-extension/src/pages/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { ChakraProvider } from '@chakra-ui/react'; -import * as ReactDOM from 'react-dom/client'; -import { createHashRouter, RouterProvider } from 'react-router-dom'; -import App from './App'; - -const rootElement = document.getElementById('root'); -const router = createHashRouter([ - { - path: '/*', - element: , - }, -]); - -rootElement && - ReactDOM.createRoot(rootElement).render( - - - - - , - ); diff --git a/packages/web-extension/src/popup/App.tsx b/packages/web-extension/src/popup/App.tsx deleted file mode 100644 index 13c860cbf4..0000000000 --- a/packages/web-extension/src/popup/App.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { useState, useEffect } from 'react'; -import Browser from 'webextension-polyfill'; -import { - Box, - Flex, - IconButton, - Link, - Spacer, - Stack, - Text, -} from '@chakra-ui/react'; -import { FiSettings, FiList, FiPause, FiPlay } from 'react-icons/fi'; -import Channel from '~/utils/channel'; -import { LocalDataKey, RecorderStatus, EventName } from '~/types'; -import type { LocalData, Session } from '~/types'; - -import { CircleButton } from '~/components/CircleButton'; -import { Timer } from './Timer'; -const RECORD_BUTTON_SIZE = 3; - -const channel = new Channel(); - -export function App() { - const [status, setStatus] = useState(RecorderStatus.IDLE); - const [errorMessage, setErrorMessage] = useState(''); - const [startTime, setStartTime] = useState(0); - const [newSession, setNewSession] = useState(null); - - useEffect(() => { - const parseStatusData = (data: LocalData[LocalDataKey.recorderStatus]) => { - const { status, startTimestamp, pausedTimestamp } = data; - setStatus(status); - if (startTimestamp && pausedTimestamp) - setStartTime(Date.now() - pausedTimestamp + startTimestamp); - else if (startTimestamp) setStartTime(startTimestamp); - }; - void Browser.storage.local.get(LocalDataKey.recorderStatus).then((data) => { - if (!data || !data[LocalDataKey.recorderStatus]) return; - parseStatusData((data as LocalData)[LocalDataKey.recorderStatus]); - }); - void Browser.storage.local.onChanged.addListener((changes) => { - if (!changes[LocalDataKey.recorderStatus]) return; - const data = changes[LocalDataKey.recorderStatus] - .newValue as LocalData[LocalDataKey.recorderStatus]; - parseStatusData(data); - if (data.errorMessage) setErrorMessage(data.errorMessage); - }); - channel.on(EventName.SessionUpdated, (data) => { - setNewSession((data as { session: Session }).session); - }); - }, []); - - return ( - - - - RRWeb Recorder - - - - { - void Browser.tabs.create({ url: '/pages/index.html#/' }); - }} - size="xs" - icon={} - aria-label={'Session List'} - title="Session List" - > - { - void Browser.runtime.openOptionsPage(); - }} - size="xs" - icon={} - aria-label={'Settings button'} - title="Settings" - > - - - {status !== RecorderStatus.IDLE && startTime && ( - - )} - - { - { - if (status === RecorderStatus.IDLE) - void channel.emit(EventName.StartButtonClicked, {}); - else void channel.emit(EventName.StopButtonClicked, {}); - }} - > - - - } - {status !== RecorderStatus.IDLE && ( - { - if (status === RecorderStatus.RECORDING) { - void channel.emit(EventName.PauseButtonClicked, {}); - } else { - void channel.emit(EventName.ResumeButtonClicked, {}); - } - }} - > - - {[RecorderStatus.PAUSED, RecorderStatus.PausedSwitch].includes( - status, - ) && ( - - )} - {status === RecorderStatus.RECORDING && ( - - )} - - - )} - - {newSession && ( - - New Session: - - {newSession.name} - - - )} - {errorMessage !== '' && ( - - {errorMessage} -
- Maybe refresh your current tab. -
- )} -
- ); -} diff --git a/packages/web-extension/src/popup/Timer.tsx b/packages/web-extension/src/popup/Timer.tsx deleted file mode 100644 index d8732e2e9b..0000000000 --- a/packages/web-extension/src/popup/Timer.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Stat, StatNumber } from '@chakra-ui/react'; -import { formatTime } from '~/utils'; - -export function Timer({ - startTime, - ticking, -}: { - startTime: number; - ticking: boolean; -}) { - const [time, setTime] = useState(Date.now() - startTime); - useEffect(() => { - if (!ticking) return; - const interval = setInterval(() => { - setTime(Date.now() - startTime); - }, 100); - return () => clearInterval(interval); - }, [startTime, ticking]); - return ( - - {formatTime(time)} - - ); -} diff --git a/packages/web-extension/src/popup/index.tsx b/packages/web-extension/src/popup/index.tsx deleted file mode 100644 index d93c01a2b0..0000000000 --- a/packages/web-extension/src/popup/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { ChakraProvider } from '@chakra-ui/react'; -import * as ReactDOM from 'react-dom/client'; -import { App } from './App'; - -const rootElement = document.getElementById('root'); - -rootElement && - ReactDOM.createRoot(rootElement).render( - - - - - , - ); diff --git a/packages/web-extension/src/popup/popup.html b/packages/web-extension/src/popup/popup.html deleted file mode 100644 index 18065c296c..0000000000 --- a/packages/web-extension/src/popup/popup.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - -
- - - diff --git a/packages/web-extension/src/public/icon128.png b/packages/web-extension/src/public/icon128.png deleted file mode 100644 index c053fb110dcffdd2fffc8688ec5b23f728a99e54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9279 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 diff --git a/packages/web-extension/src/public/icon16.png b/packages/web-extension/src/public/icon16.png deleted file mode 100644 index 26a6bdd0185253e58d398f7b4293e31596b564b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3733 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 diff --git a/packages/web-extension/src/public/icon48.png b/packages/web-extension/src/public/icon48.png deleted file mode 100644 index 31d04fa38a0d0443345960fe9d802cdacc410c20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4862 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 Promise - >(); - private emitter = mitt(); - constructor() { - /** - * Register message listener. - */ - Browser.runtime.onMessage.addListener( - ((message: string, sender: Runtime.MessageSender) => { - const parsed = JSON.parse(message) as Message | null | undefined; - if (!parsed || !parsed.type) { - console.error(`Bad message: ${message}`); - return; - } - switch (parsed.type) { - case 'event': - this.emitter.emit(parsed.event, { detail: parsed.detail, sender }); - break; - case 'service': { - const server = this.services.get(parsed.service); - if (!server) break; - return server(parsed.params, sender); - } - default: - console.error( - `Unknown message type: ${(parsed as { type: string }).type}`, - ); - break; - } - return; - }).bind(this), - ); - } - - /** - * Provide a service. - * - * @param serviceName - the name of the service, acts like a URL - * @param serveFunction - a function to provide the service when a consumer request this service. - * @returns a function to remove the service - */ - public provide( - serviceName: string, - serveFunction: ( - params: unknown, - sender: Runtime.MessageSender, - ) => Promise, - ): () => void { - this.services.set(serviceName, serveFunction); - return () => { - this.services.delete(serviceName); - }; - } - - /** - * Send a request and get a response. - * - * @param service - service name to request - * @param params - request parameters - * @returns service data - */ - public request( - serviceName: string, - params: Record | unknown, - ) { - const message = JSON.stringify({ - type: 'service', - service: serviceName, - params, - }); - return Browser.runtime.sendMessage(message); - } - - /** - * Send a request to the specified tab and get a response. - * - * @param tabId - tab id - * @param service - service name to request - * @param params - request parameters - * @returns service data - */ - public requestToTab( - tabId: number, - serviceName: string, - params: Record | unknown, - ) { - if (!Browser.tabs || !Browser.tabs.sendMessage) - return Promise.reject('Can not send message to tabs in current context!'); - const message = JSON.stringify({ - type: 'service', - service: serviceName, - params, - }); - return Browser.tabs.sendMessage(tabId, message); - } - - /** - * Add an event handler. - * - * @param eventName - event name - * @param handler - event handler, accepts two arguments: - * detail: event detail - * source: source of the event, Browser.runtime.MessageSender object - * @returns a function to remove the handler - */ - public on( - event: string, - handler: (detail: unknown, sender: Runtime.MessageSender) => unknown, - ) { - const emitHandler = ((data: { - detail: unknown; - sender: Runtime.MessageSender; - }) => { - handler(data.detail, data.sender); - }) as (data: unknown) => unknown; - return this.emitter.on(event, emitHandler); - } - - /** - * Emit an event. - * - * @param event - event name - * @param detail - event detail - */ - public emit(event: string, detail: unknown) { - const message = JSON.stringify({ type: 'event', event, detail }); - void Browser.runtime.sendMessage(message); - } - - /** - * Emit an event to specified tabs. - * - * @param tabIds - tab ids - * @param event - event name - * @param detail - event detail - */ - public emitToTabs(tabIds: number | number[], event: string, detail: unknown) { - if (!Browser.tabs || !Browser.tabs.sendMessage) - return Promise.reject('Can not send message to tabs in current context!'); - - // If tabIds is a number, wrap it up with an array. - if (typeof tabIds === 'number') { - tabIds = [tabIds]; - } - - const message = JSON.stringify({ type: 'event', event, detail }); - tabIds.forEach((tabId) => void Browser.tabs.sendMessage(tabId, message)); - } - - public async getCurrentTabId() { - const tabs = await Browser.tabs.query({ - active: true, - currentWindow: true, - }); - return tabs[0].id || -1; - } -} - -export default Channel; diff --git a/packages/web-extension/src/utils/index.ts b/packages/web-extension/src/utils/index.ts deleted file mode 100644 index 379fc21070..0000000000 --- a/packages/web-extension/src/utils/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -export function isFirefox(): boolean { - return ( - (typeof window !== 'undefined' && - window.navigator?.userAgent.toLowerCase().includes('firefox')) || - false - ); -} - -export function isInCrossOriginIFrame(): boolean { - if (window.parent !== window) { - try { - void window.parent.location.origin; - } catch (error) { - return true; - } - } - return false; -} - -const SECOND = 1000; -const MINUTE = 60 * SECOND; -const HOUR = 60 * MINUTE; - -export function formatTime(ms: number): string { - if (ms <= 0) { - return '00:00'; - } - const hour = Math.floor(ms / HOUR); - ms = ms % HOUR; - const minute = Math.floor(ms / MINUTE); - ms = ms % MINUTE; - const second = Math.floor(ms / SECOND); - if (hour) { - return `${padZero(hour)}:${padZero(minute)}:${padZero(second)}`; - } - return `${padZero(minute)}:${padZero(second)}`; -} - -function padZero(num: number, len = 2): string { - let str = String(num); - const threshold = Math.pow(10, len - 1); - if (num < threshold) { - while (String(threshold).length > str.length) { - str = `0${num}`; - } - } - return str; -} diff --git a/packages/web-extension/src/utils/storage.ts b/packages/web-extension/src/utils/storage.ts deleted file mode 100644 index 0050bf084d..0000000000 --- a/packages/web-extension/src/utils/storage.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { openDB } from 'idb'; -import type { eventWithTime } from '@saola.ai/rrweb-types'; -import type { Session } from '~/types'; - -/** - * Storage related functions with indexedDB. - */ - -const EventStoreName = 'events'; -type EventData = { - id: string; - events: eventWithTime[]; -}; - -export async function getEventStore() { - return openDB(EventStoreName, 1, { - upgrade(db) { - db.createObjectStore(EventStoreName, { - keyPath: 'id', - autoIncrement: false, - }); - }, - }); -} - -export async function getEvents(id: string) { - const db = await getEventStore(); - const data = (await db.get(EventStoreName, id)) as EventData; - return data.events; -} - -const SessionStoreName = 'sessions'; -export async function getSessionStore() { - return openDB(SessionStoreName, 1, { - upgrade(db) { - // Create a store of objects - db.createObjectStore(SessionStoreName, { - // The 'id' property of the object will be the key. - keyPath: 'id', - // If it isn't explicitly set, create a value by auto incrementing. - autoIncrement: false, - }); - }, - }); -} - -export async function addSession(session: Session, events: eventWithTime[]) { - const eventStore = await getEventStore(); - await eventStore.put(EventStoreName, { id: session.id, events }); - const store = await getSessionStore(); - await store.add(SessionStoreName, session); -} - -export async function updateSession( - session: Session, - events?: eventWithTime[], -) { - const eventStore = await getEventStore(); - if (events) { - await eventStore.put(EventStoreName, { id: session.id, events }); - } - const store = await getSessionStore(); - await store.put(SessionStoreName, session); -} - -export async function getSession(id: string) { - const store = await getSessionStore(); - return store.get(SessionStoreName, id) as Promise; -} - -export async function getAllSessions() { - const store = await getSessionStore(); - const sessions = (await store.getAll(SessionStoreName)) as Session[]; - return sessions.sort((a, b) => b.createTimestamp - a.createTimestamp); -} - -export async function deleteSession(id: string) { - const eventStore = await getEventStore(); - const sessionStore = await getSessionStore(); - await Promise.all([ - eventStore.delete(EventStoreName, id), - sessionStore.delete(SessionStoreName, id), - ]); -} - -export async function deleteSessions(ids: string[]) { - const eventStore = await getEventStore(); - const sessionStore = await getSessionStore(); - const eventTransition = eventStore.transaction(EventStoreName, 'readwrite'); - const sessionTransition = sessionStore.transaction( - SessionStoreName, - 'readwrite', - ); - const promises = []; - for (const id of ids) { - promises.push(eventTransition.store.delete(id)); - promises.push(sessionTransition.store.delete(id)); - } - await Promise.all(promises).then(() => { - return Promise.all([eventTransition.done, sessionTransition.done]); - }); -} - -export async function downloadSessions(ids: string[]) { - for (const sessionId of ids) { - const events = await getEvents(sessionId); - const session = await getSession(sessionId); - const blob = new Blob([JSON.stringify({ session, events }, null, 2)], { - type: 'application/json', - }); - - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `${session.name}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - } -} diff --git a/packages/web-extension/tsconfig.json b/packages/web-extension/tsconfig.json deleted file mode 100644 index 759f972d89..0000000000 --- a/packages/web-extension/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "baseUrl": ".", - "tsBuildInfoFile": "./tsconfig.tsbuildinfo", - "esModuleInterop": true, - "incremental": true, - "resolveJsonModule": true, - "paths": { - "~/*": ["src/*"] - }, - "jsx": "react-jsx" - }, - "exclude": ["dist", "node_modules", "vite.config.ts"], - "references": [ - { - "path": "../rrweb" - }, - { - "path": "../rrweb-player" - }, - { - "path": "../types" - } - ] -} diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts deleted file mode 100644 index 17f855a276..0000000000 --- a/packages/web-extension/vite.config.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { defineConfig, LibraryFormats, PluginOption } from 'vite'; -import webExtension, { readJsonFile } from 'vite-plugin-web-extension'; -import zip from 'vite-plugin-zip-pack'; -import * as path from 'path'; -import type { PackageJson } from 'type-fest'; -import react from '@vitejs/plugin-react'; -import semver from 'semver'; - -const emptyOutDir = !process.argv.includes('--watch'); - -function useSpecialFormat( - entriesToUse: string[], - format: LibraryFormats, -): PluginOption { - return { - name: 'use-special-format', - config(config) { - // entry can be string | string[] | {[entryAlias: string]: string} - const entry = config.build?.lib && config.build.lib.entry; - let shouldUse = false; - - if (typeof entry === 'string') { - shouldUse = entriesToUse.includes(entry); - } else if (Array.isArray(entry)) { - shouldUse = entriesToUse.some((e) => entry.includes(e)); - } else if (entry && typeof entry === 'object') { - const entryKeys = Object.keys(entry); - shouldUse = entriesToUse.some((e) => entryKeys.includes(e)); - } - - if (shouldUse) { - config.build = config.build ?? {}; - // @ts-expect-error: lib needs to be an object, forcing it. - config.build.lib = - typeof config.build.lib == 'object' ? config.build.lib : {}; - // @ts-expect-error: lib is an object - config.build.lib.formats = [format]; - } - }, - }; -} - -/** - * Get the extension version based on the rrweb version. - */ -function getExtensionVersion(rrwebVersion: string): string { - const parsedVersion = semver.parse(rrwebVersion.replace('^', '')); - - if (!parsedVersion) { - throw new Error('Invalid version format'); - } - - if (parsedVersion.prerelease.length > 0) { - // If it's a pre-release version like alpha or beta, strip the pre-release identifier - return `${parsedVersion.major}.${parsedVersion.minor}.${ - parsedVersion.patch - }.${parsedVersion.prerelease[1] || 0}`; - } else if (rrwebVersion === '2.0.0') { - // This version has already been released as the first version. We need to add a patch version to it to avoid publishing conflicts. - return '2.0.0.100'; - } else { - return rrwebVersion; - } -} - -export default defineConfig({ - root: 'src', - // Configure our outputs - nothing special, this is normal vite config - build: { - outDir: path.resolve(__dirname, 'dist', 'chrome'), - emptyOutDir, - }, - // Add the webExtension plugin - plugins: [ - react(), - webExtension({ - // A function to generate manifest file dynamically. - manifest: () => { - const packageJson = readJsonFile('package.json') as PackageJson; - type ManifestBase = { - common: Record; - chrome: Record; - firefox: Record; - }; - const originalManifest = readJsonFile('./src/manifest.json') as { - common: Record; - v2: ManifestBase; - v3: ManifestBase; - }; - const ManifestVersion = - process.env.TARGET_BROWSER === 'chrome' ? 'v3' : 'v2'; - const BrowserName = - process.env.TARGET_BROWSER === 'chrome' ? 'chrome' : 'firefox'; - const commonManifest = originalManifest.common; - const rrwebVersion = packageJson.dependencies!.rrweb!.replace('^', ''); - const manifest = { - version: getExtensionVersion(rrwebVersion), - author: packageJson.author, - version_name: rrwebVersion, - ...commonManifest, - }; - Object.assign( - manifest, - originalManifest[ManifestVersion].common, - originalManifest[ManifestVersion][BrowserName], - ); - return manifest; - }, - browser: process.env.TARGET_BROWSER, - webExtConfig: { - startUrl: ['github.com/rrweb-io/rrweb'], - watchIgnored: ['*.md', '*.log'], - }, - additionalInputs: ['pages/index.html', 'content/inject.ts'], - }) as PluginOption, - // https://github.com/aklinker1/vite-plugin-web-extension/issues/50#issuecomment-1317922947 - // transfer inject.ts to iife format to avoid error - useSpecialFormat( - [path.resolve(__dirname, 'src/content/inject.ts')], - 'iife', - ), - process.env.ZIP === 'true' && - zip({ - inDir: `dist/${process.env.TARGET_BROWSER || 'chrome'}`, - outDir: 'dist', - outFileName: `${process.env.TARGET_BROWSER || 'chrome'}.zip`, - }), - ], - resolve: { - alias: { - '~': path.resolve(__dirname, './src'), - }, - }, -}); From 880b67bfbb97c74b9814d36f1d7021553026dafc Mon Sep 17 00:00:00 2001 From: Shay Malchi Date: Wed, 29 Jan 2025 15:15:34 +0200 Subject: [PATCH 33/34] remove changeset md --- .changeset/beige-olives-roll.md | 6 ------ .changeset/blank-cherries-laugh.md | 2 -- .changeset/bright-socks-clap.md | 2 -- .changeset/chilled-penguins-sin.md | 5 ----- .changeset/cool-horses-bow.md | 14 -------------- .changeset/cuddly-bikes-fail.md | 6 ------ .changeset/efficiently-splitCssText-1603.md | 6 ------ .changeset/eslint-camelcase-devonly.md | 2 -- .changeset/four-panthers-fly.md | 5 ----- .changeset/giant-rats-chew.md | 5 ----- .changeset/gold-experts-type.md | 5 ----- .changeset/great-cows-camp.md | 6 ------ .changeset/happy-carrots-hide.md | 5 ----- .changeset/last-jest-to-vitest.md | 2 -- .changeset/metal-mugs-mate.md | 2 -- .changeset/moody-experts-build.md | 5 ----- .changeset/odd-onions-brush.md | 5 ----- .changeset/perfect-dolls-grab.md | 5 ----- .changeset/red-peaches-explode.md | 5 ----- .changeset/silly-knives-chew.md | 7 ------- .changeset/simplifify-hover-replacement.md | 6 ------ .changeset/single-style-capture.md | 6 ------ .changeset/six-llamas-brush.md | 5 ----- .changeset/skip-mask-check-on-leaf-elements.md | 6 ------ .changeset/soft-worms-tan.md | 11 ----------- .changeset/swift-pots-search.md | 5 ----- .changeset/two-boats-boil.md | 2 -- .changeset/unlucky-mirrors-invite.md | 7 ------- .changeset/wicked-dolphins-tie.md | 5 ----- .changeset/wicked-lions-return.md | 2 -- 30 files changed, 155 deletions(-) delete mode 100644 .changeset/beige-olives-roll.md delete mode 100644 .changeset/blank-cherries-laugh.md delete mode 100644 .changeset/bright-socks-clap.md delete mode 100644 .changeset/chilled-penguins-sin.md delete mode 100644 .changeset/cool-horses-bow.md delete mode 100644 .changeset/cuddly-bikes-fail.md delete mode 100644 .changeset/efficiently-splitCssText-1603.md delete mode 100644 .changeset/eslint-camelcase-devonly.md delete mode 100644 .changeset/four-panthers-fly.md delete mode 100644 .changeset/giant-rats-chew.md delete mode 100644 .changeset/gold-experts-type.md delete mode 100644 .changeset/great-cows-camp.md delete mode 100644 .changeset/happy-carrots-hide.md delete mode 100644 .changeset/last-jest-to-vitest.md delete mode 100644 .changeset/metal-mugs-mate.md delete mode 100644 .changeset/moody-experts-build.md delete mode 100644 .changeset/odd-onions-brush.md delete mode 100644 .changeset/perfect-dolls-grab.md delete mode 100644 .changeset/red-peaches-explode.md delete mode 100644 .changeset/silly-knives-chew.md delete mode 100644 .changeset/simplifify-hover-replacement.md delete mode 100644 .changeset/single-style-capture.md delete mode 100644 .changeset/six-llamas-brush.md delete mode 100644 .changeset/skip-mask-check-on-leaf-elements.md delete mode 100644 .changeset/soft-worms-tan.md delete mode 100644 .changeset/swift-pots-search.md delete mode 100644 .changeset/two-boats-boil.md delete mode 100644 .changeset/unlucky-mirrors-invite.md delete mode 100644 .changeset/wicked-dolphins-tie.md delete mode 100644 .changeset/wicked-lions-return.md diff --git a/.changeset/beige-olives-roll.md b/.changeset/beige-olives-roll.md deleted file mode 100644 index 4707f55aca..0000000000 --- a/.changeset/beige-olives-roll.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"rrweb-snapshot": patch -"rrweb": patch ---- - -Fix that the optional `maskInputFn` was being accidentally ignored during the creation of the full snapshot diff --git a/.changeset/blank-cherries-laugh.md b/.changeset/blank-cherries-laugh.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/blank-cherries-laugh.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/bright-socks-clap.md b/.changeset/bright-socks-clap.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/bright-socks-clap.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/chilled-penguins-sin.md b/.changeset/chilled-penguins-sin.md deleted file mode 100644 index 060744a07b..0000000000 --- a/.changeset/chilled-penguins-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"rrdom": patch ---- - -Ignore invalid DOM attributes when diffing diff --git a/.changeset/cool-horses-bow.md b/.changeset/cool-horses-bow.md deleted file mode 100644 index 220bb217a9..0000000000 --- a/.changeset/cool-horses-bow.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -"@rrweb/rrweb-plugin-canvas-webrtc-record": patch -"@rrweb/rrweb-plugin-canvas-webrtc-replay": patch -"@rrweb/rrweb-plugin-sequential-id-record": patch -"@rrweb/rrweb-plugin-sequential-id-replay": patch -"@rrweb/rrweb-plugin-console-record": patch -"@rrweb/rrweb-plugin-console-replay": patch -"@rrweb/packer": patch -"@rrweb/record": patch -"@rrweb/replay": patch -"@rrweb/all": patch ---- - -Keep package version in sync with other packages diff --git a/.changeset/cuddly-bikes-fail.md b/.changeset/cuddly-bikes-fail.md deleted file mode 100644 index 0c99160c9e..0000000000 --- a/.changeset/cuddly-bikes-fail.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"rrweb-snapshot": patch -"rrweb": patch ---- - -fix: duplicate textContent for style elements cause incremental style mutations to be invalid diff --git a/.changeset/efficiently-splitCssText-1603.md b/.changeset/efficiently-splitCssText-1603.md deleted file mode 100644 index 57c6d5e6c4..0000000000 --- a/.changeset/efficiently-splitCssText-1603.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"rrweb-snapshot": patch -"rrweb": patch ---- - -Improve performance of splitCssText for