diff --git a/.changeset/config.json b/.changeset/config.json index 89ad04bbc7..844c3277b6 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -4,25 +4,24 @@ "commit": false, "fixed": [ [ - "rrweb", - "rrweb-snapshot", - "rrdom", - "rrdom-nodejs", - "rrweb-player", - "@rrweb/all", - "@rrweb/replay", - "@rrweb/record", - "@rrweb/types", - "@rrweb/packer", - "@rrweb/utils", - "@rrweb/web-extension", - "rrvideo", - "@rrweb/rrweb-plugin-console-record", - "@rrweb/rrweb-plugin-console-replay", - "@rrweb/rrweb-plugin-sequential-id-record", - "@rrweb/rrweb-plugin-sequential-id-replay", - "@rrweb/rrweb-plugin-canvas-webrtc-record", - "@rrweb/rrweb-plugin-canvas-webrtc-replay" + "@mixpanel/rrweb", + "@mixpanel/rrweb-snapshot", + "@mixpanel/rrdom", + "@mixpanel/rrdom-nodejs", + "@mixpanel/rrweb-player", + "@mixpanel/rrweb-all", + "@mixpanel/rrweb-replay", + "@mixpanel/rrweb-record", + "@mixpanel/rrweb-types", + "@mixpanel/rrweb-packer", + "@mixpanel/rrweb-utils", + "@mixpanel/rrvideo", + "@mixpanel/rrweb-plugin-console-record", + "@mixpanel/rrweb-plugin-console-replay", + "@mixpanel/rrweb-plugin-sequential-id-record", + "@mixpanel/rrweb-plugin-sequential-id-replay", + "@mixpanel/rrweb-plugin-canvas-webrtc-record", + "@mixpanel/rrweb-plugin-canvas-webrtc-replay" ] ], "linked": [], diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index f51b628367..35dd847462 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -12,13 +12,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - name: Setup Node.js lts/* - uses: actions/setup-node@v3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version: lts/* cache: 'yarn' @@ -39,7 +39,7 @@ jobs: run: PUPPETEER_HEADLESS=true xvfb-run --server-args="-screen 0 1920x1080x24" yarn test - name: Check bundle sizes - uses: preactjs/compressed-size-action@v2 + uses: preactjs/compressed-size-action@f322c295dde06a1cb7ccaef105732dd8a726d1d9 with: install-script: 'yarn install --frozen-lockfile' build-script: 'build:all' @@ -49,7 +49,7 @@ jobs: PUPPETEER_SKIP_DOWNLOAD: true - name: Upload diff images to GitHub - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 if: failure() with: name: image-diff diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a351521391..8ae3ed4b85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 - name: Setup Node.js lts/* - uses: actions/setup-node@v3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version: lts/* @@ -27,7 +27,7 @@ jobs: - name: Create Release Pull Request or Publish to npm id: changesets - uses: changesets/action@v1 + uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d with: publish: yarn run release env: @@ -40,7 +40,7 @@ jobs: run: NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension - name: Publish Chrome Extension - uses: mnao305/chrome-extension-upload@v5.0.0 + uses: mnao305/chrome-extension-upload@4008e29e13c144d0f6725462cbd49b7c291b4928 if: steps.changesets.outputs.published == 'true' with: extension-id: 'pdaldeopoccdhlkabbkcjmecmmoninhe' diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index a37b1a45a7..4362d9568d 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -10,12 +10,12 @@ jobs: name: ESLint Check and Report Upload steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.head_ref }} - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version: lts/* cache: 'yarn' @@ -32,7 +32,7 @@ jobs: # Continue to the next step even if this fails continue-on-error: true - name: Upload ESLint Report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: name: eslint_report.json path: eslint_report.json @@ -46,11 +46,11 @@ jobs: runs-on: ubuntu-latest name: ESLint Annotation steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 with: name: eslint_report.json - name: Annotate Code Linting Results - uses: ataylorme/eslint-annotate-action@v2 + uses: ataylorme/eslint-annotate-action@5f4dc2e3af8d3c21b727edb597e5503510b1dc9c with: repo-token: '${{ secrets.GITHUB_TOKEN }}' report-json: 'eslint_report.json' @@ -63,12 +63,12 @@ jobs: runs-on: ubuntu-latest name: Format Check steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.head_ref }} - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version: lts/* cache: 'yarn' @@ -87,12 +87,12 @@ jobs: contents: write name: Format Code steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.head_ref }} - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version: lts/* cache: 'yarn' @@ -103,7 +103,7 @@ jobs: - name: Prettify Code run: yarn prettier --write '**/*.{ts,md}' - name: Commit Changes - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a with: commit_message: Apply formatting changes branch: ${{ github.head_ref }} diff --git a/package.json b/package.json index 60e7b9e5b9..8273426e59 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "@rrweb/_monorepo", + "name": "@mixpanel/rrweb_monorepo", "description": "record and replay the web", "repository": { "type": "git", - "url": "git+ssh://git@github.com/rrweb-io/rrweb.git" + "url": "git+ssh://git@github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" @@ -11,10 +11,10 @@ "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "private": true, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "workspaces": [ "packages/*", "packages/plugins/*" @@ -56,7 +56,8 @@ "live-stream": "cd packages/rrweb && yarn live-stream", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'ESLINT_USE_FLAT_CONFIG=false yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'", "lint:report": "ESLINT_USE_FLAT_CONFIG=false yarn eslint --output-file eslint_report.json --format json packages/*/src --ext .ts,.tsx,.js,.jsx", - "release": "yarn build:all && changeset publish" + "release": "yarn build:all && changeset publish", + "mixpanel-version": "node write-mixpanel-version.js" }, "resolutions": { "**/cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", diff --git a/packages/all/package.json b/packages/all/package.json index 6aa8256457..925e43a2a7 100644 --- a/packages/all/package.json +++ b/packages/all/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/all", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-all", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -17,29 +17,29 @@ "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/all#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/all#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/all.cjs", - "module": "./dist/all.js", - "unpkg": "./dist/all.umd.cjs", + "main": "./dist/rrweb-all.cjs", + "module": "./dist/rrweb-all.js", + "unpkg": "./dist/rrweb-all.umd.cjs", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/all.js" + "default": "./dist/rrweb-all.js" }, "require": { "types": "./dist/index.d.cts", - "default": "./dist/all.cjs" + "default": "./dist/rrweb-all.cjs" } } }, @@ -56,9 +56,9 @@ "typescript": "^5.4.5" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.18", - "@rrweb/packer": "^2.0.0-alpha.18", - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-packer": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18" }, "browserslist": [ "supports es6-class" diff --git a/packages/all/src/index.ts b/packages/all/src/index.ts index aa01eeaba9..5f972cdf49 100644 --- a/packages/all/src/index.ts +++ b/packages/all/src/index.ts @@ -1,4 +1,4 @@ -export * from 'rrweb'; -export * from '@rrweb/packer'; -// export * from '@rrweb/rrweb-plugin-console-record'; -// export * from '@rrweb/rrweb-plugin-console-replay'; +export * from '@mixpanel/rrweb'; +export * from '@mixpanel/rrweb-packer'; +// export * from '@mixpanel/rrweb-plugin-console-record'; +// export * from '@mixpanel/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 2623285537..3fe583fa9f 100644 --- a/packages/all/test/cross-origin-iframe-packer.test.ts +++ b/packages/all/test/cross-origin-iframe-packer.test.ts @@ -50,35 +50,42 @@ interface IWindow extends Window { } type ExtraOptions = { usePackFn?: boolean; + crossOrigin?: boolean; }; async function injectRecordScript( frame: puppeteer.Frame, options?: ExtraOptions, + allowedOrigins?: string[], ) { await frame.addScriptTag({ - path: path.resolve(__dirname, '../dist/all.umd.cjs'), + path: path.resolve(__dirname, '../dist/rrweb-all.umd.cjs'), }); options = options || {}; - await frame.evaluate((options) => { - (window as unknown as IWindow).snapshots = []; - const { record, pack } = (window as unknown as IWindow).rrweb; - const config: recordOptions = { - recordCrossOriginIframes: true, - recordCanvas: true, - emit(event) { - (window as unknown as IWindow).snapshots.push(event); - (window as unknown as IWindow).emit(event); - }, - }; - if (options.usePackFn) { - config.packFn = pack; - } - record(config); - }, options); + await frame.evaluate( + (options, allowedOrigins) => { + (window as unknown as IWindow).snapshots = []; + const { record, pack } = (window as unknown as IWindow).rrweb; + const config: recordOptions = { + recordCrossOriginIframes: !!allowedOrigins, + recordCanvas: true, + allowedIframeOrigins: allowedOrigins, + emit(event) { + (window as unknown as IWindow).snapshots.push(event); + (window as unknown as IWindow).emit(event); + }, + }; + if (options.usePackFn) { + config.packFn = pack; + } + record(config); + }, + options, + allowedOrigins, + ); for (const child of frame.childFrames()) { - await injectRecordScript(child, options); + await injectRecordScript(child, options, allowedOrigins); } } @@ -87,19 +94,24 @@ const setup = function ( content: string, options?: ExtraOptions, ): ISuite { - const ctx = {} as ISuite; + const ctx = {} as ISuite & { + serverB: http.Server; + serverBURL: string; + }; beforeAll(async () => { ctx.browser = await launchPuppeteer(); ctx.server = await startServer(); ctx.serverURL = getServerURL(ctx.server); + ctx.serverB = await startServer(); + ctx.serverBURL = getServerURL(ctx.serverB); }); beforeEach(async () => { ctx.page = await ctx.browser.newPage(); - await ctx.page.goto('about:blank'); + await ctx.page.goto(`${ctx.serverURL}/html/blank.html`); await ctx.page.setContent( - content.replace(/\{SERVER_URL\}/g, ctx.serverURL), + content.replace(/\{SERVER_URL\}/g, ctx.serverBURL), ); ctx.events = []; await ctx.page.exposeFunction('emit', (e: eventWithTime) => { @@ -110,7 +122,10 @@ const setup = function ( }); ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); - await injectRecordScript(ctx.page.mainFrame(), options); + const allowedOrigins = options?.crossOrigin + ? [ctx.serverURL, ctx.serverBURL] + : undefined; + await injectRecordScript(ctx.page.mainFrame(), options, allowedOrigins); }); afterEach(async () => { @@ -120,6 +135,7 @@ const setup = function ( afterAll(async () => { await ctx.browser.close(); ctx.server.close(); + ctx.serverB.close(); }); return ctx; @@ -137,7 +153,10 @@ describe('cross origin iframes & packer', function (this: ISuite) { `; - const ctx = setup.call(this, content, { usePackFn: true }); + const ctx = setup.call(this, content, { + usePackFn: true, + crossOrigin: true, + }); describe('should support packFn option in record()', () => { it('', async () => { diff --git a/packages/packer/package.json b/packages/packer/package.json index c4595a4e87..d9079f13d1 100644 --- a/packages/packer/package.json +++ b/packages/packer/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/packer", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-packer", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -17,19 +17,19 @@ "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/packer#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/packer#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/packer.cjs", - "module": "./dist/packer.js", - "unpkg": "./dist/packer.js", + "main": "./dist/rrweb-packer.cjs", + "module": "./dist/rrweb-packer.js", + "unpkg": "./dist/rrweb-packer.js", "typings": "dist/packer.d.ts", "exports": { ".": { @@ -78,7 +78,7 @@ }, "dependencies": { "fflate": "^0.4.4", - "@rrweb/types": "^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "^2.0.0-alpha.18" }, "browserslist": [ "supports es6-class" diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-record/package.json index c5c0c38e29..efa2392e19 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": "@rrweb/rrweb-plugin-canvas-webrtc-record", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-canvas-webrtc-record", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-canvas-webrtc-record.umd.cjs", @@ -31,7 +31,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" @@ -39,16 +39,16 @@ "author": "justin@recordonce.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18" } } diff --git a/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json b/packages/plugins/rrweb-plugin-canvas-webrtc-replay/package.json index 901039cf78..b5193da486 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": "@rrweb/rrweb-plugin-canvas-webrtc-replay", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-canvas-webrtc-replay", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-canvas-webrtc-replay.umd.cjs", @@ -31,7 +31,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" @@ -39,16 +39,16 @@ "author": "justin@recordonce.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18" } } diff --git a/packages/plugins/rrweb-plugin-console-record/package.json b/packages/plugins/rrweb-plugin-console-record/package.json index 07897f39dd..caf8aca646 100644 --- a/packages/plugins/rrweb-plugin-console-record/package.json +++ b/packages/plugins/rrweb-plugin-console-record/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/rrweb-plugin-console-record", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-console-record", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-console-record.umd.cjs", @@ -33,19 +33,19 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" ], - "author": "yanzhen@smartx.com", + "author": "justin@recordonce.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1", @@ -53,7 +53,7 @@ "puppeteer": "^20.9.0" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18", - "@rrweb/utils": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb-utils": "^2.0.0-alpha.18" } } diff --git a/packages/plugins/rrweb-plugin-console-replay/package.json b/packages/plugins/rrweb-plugin-console-replay/package.json index 2484fd7eb4..8d8719a701 100644 --- a/packages/plugins/rrweb-plugin-console-replay/package.json +++ b/packages/plugins/rrweb-plugin-console-replay/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/rrweb-plugin-console-replay", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-console-replay", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-console-replay.umd.cjs", @@ -31,7 +31,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" @@ -39,17 +39,17 @@ "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "devDependencies": { - "@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.18", - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb-plugin-console-record": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18" } } diff --git a/packages/plugins/rrweb-plugin-sequential-id-record/package.json b/packages/plugins/rrweb-plugin-sequential-id-record/package.json index dc3fc37a15..b7922bd74a 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": "@rrweb/rrweb-plugin-sequential-id-record", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-sequential-id-record", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-sequential-id-record.umd.cjs", @@ -43,12 +43,12 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18" } } diff --git a/packages/plugins/rrweb-plugin-sequential-id-replay/package.json b/packages/plugins/rrweb-plugin-sequential-id-replay/package.json index 0538a6db52..0bc06baa79 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": "@rrweb/rrweb-plugin-sequential-id-replay", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-plugin-sequential-id-replay", + "version": "2.0.0-alpha.18.6", "description": "", "type": "module", "main": "./dist/rrweb-plugin-sequential-id-replay.umd.cjs", @@ -43,13 +43,13 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@rrweb/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.18", - "rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", "typescript": "^5.4.5", "vite": "^5.3.1", "vite-plugin-dts": "^3.9.1" }, "peerDependencies": { - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18" } } diff --git a/packages/record/package.json b/packages/record/package.json index 6b1d244d6f..e03f9b197b 100644 --- a/packages/record/package.json +++ b/packages/record/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/record", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-record", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -17,29 +17,29 @@ "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/record#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/record#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/record.cjs", - "module": "./dist/record.js", - "unpkg": "./dist/record.umd.cjs", + "main": "./dist/rrweb-record.cjs", + "module": "./dist/rrweb-record.js", + "unpkg": "./dist/rrweb-record.umd.cjs", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/record.js" + "default": "./dist/rrweb-record.js" }, "require": { "types": "./dist/index.d.cts", - "default": "./dist/record.cjs" + "default": "./dist/rrweb-record.cjs" } } }, @@ -55,9 +55,9 @@ "typescript": "^5.4.5" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.18", - "rrweb": "^2.0.0-alpha.18", - "@rrweb/utils": "^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb-utils": "^2.0.0-alpha.18" }, "browserslist": [ "supports es6-class" diff --git a/packages/replay/package.json b/packages/replay/package.json index 5c9d3f44e9..b5dae3389b 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/replay", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-replay", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -17,29 +17,29 @@ "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/replay#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/replay#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/replay.cjs", - "module": "./dist/replay.js", - "unpkg": "./dist/replay.umd.cjs", + "main": "./dist/rrweb-replay.cjs", + "module": "./dist/rrweb-replay.js", + "unpkg": "./dist/rrweb-replay.umd.cjs", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/replay.js" + "default": "./dist/rrweb-replay.js" }, "require": { "types": "./dist/index.d.cts", - "default": "./dist/replay.cjs" + "default": "./dist/rrweb-replay.cjs" } }, "./dist/style.css": "./dist/style.css" @@ -56,8 +56,8 @@ "typescript": "^5.4.5" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.18", - "rrweb": "^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", + "@mixpanel/rrweb": "^2.0.0-alpha.18" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 455c317a7c..176b359d28 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { - "name": "rrdom-nodejs", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrdom-nodejs", + "version": "2.0.0-alpha.18.6", "scripts": { "dev": "vite build --watch", "build": "yarn turbo run prepublish", @@ -55,7 +55,7 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "2.2.0", - "rrdom": "^2.0.0-alpha.18", - "@rrweb/types": "^2.0.0-alpha.18" + "@mixpanel/rrdom": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18" } } diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 9657f17209..a4674f2003 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,7 +1,7 @@ { - "name": "rrdom", - "version": "2.0.0-alpha.18", - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", + "name": "@mixpanel/rrdom", + "version": "2.0.0-alpha.18.6", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "type": "module", "main": "./dist/rrdom.cjs", @@ -26,7 +26,7 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "scripts": { "dev": "vite build --watch", @@ -38,10 +38,10 @@ "lint": "yarn eslint src/**/*.ts" }, "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", "@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.9.1" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.18" + "@mixpanel/rrweb-snapshot": "^2.0.0-alpha.18" } } diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index e5984e580a..2222e0ed97 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -368,7 +368,7 @@ export class Mirror implements IMirror { // removes the node from idNodeMap // doesn't remove the node from nodeMetaMap - removeNodeFromMap(n: RRNode) { + removeNodeFromMap(n: RRNode, _permanent?: boolean) { const id = this.getId(n); this.idNodeMap.delete(id); diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index 996184dee2..dfecdd9759 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { - "name": "rrvideo", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrvideo", + "version": "2.0.0-alpha.18.6", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -27,7 +27,7 @@ "@types/node": "^18.15.11", "jest": "^27.5.1", "ts-jest": "^27.1.3", - "@rrweb/types": "^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "^2.0.0-alpha.18" }, "dependencies": { "@open-tech-world/cli-progress-bar": "^2.0.2", diff --git a/packages/rrvideo/tsconfig.json b/packages/rrvideo/tsconfig.json index 95955dda4a..dba56dda29 100644 --- a/packages/rrvideo/tsconfig.json +++ b/packages/rrvideo/tsconfig.json @@ -16,11 +16,12 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "exclude": ["build", "node_modules", "test"], + "exclude": [ + "build", + "node_modules", + "test" + ], "references": [ - { - "path": "../rrweb-player" - }, { "path": "../types" } diff --git a/packages/rrweb-player/.svelte-kit/ambient.d.ts b/packages/rrweb-player/.svelte-kit/ambient.d.ts index 436172dd51..db6c9f8761 100644 --- a/packages/rrweb-player/.svelte-kit/ambient.d.ts +++ b/packages/rrweb-player/.svelte-kit/ambient.d.ts @@ -26,217 +26,169 @@ * ``` */ declare module '$env/static/private' { - export const GITHUB_STATE: string; - export const GIT_CLONE_PROTECTION_ACTIVE: string; + export const PYENV_VIRTUALENV_INIT: string; + export const ANTHROPIC_VERTEX_PROJECT_ID: string; + export const LESSOPEN: string; export const npm_package_scripts_test_cross_platform_build: string; export const npm_package_devDependencies_rollup: string; export const npm_package_devDependencies__types_node: string; - export const STATS_TRP: string; - export const DEPLOYMENT_BASEPATH: string; - export const DOTNET_NOLOGO: string; export const npm_package_devDependencies_vitest: string; + export const TFLINT_OPA_POLICY_DIR: string; export const USER: string; + export const SSH_CLIENT: string; + export const npm_config_user_agent: string; export const npm_package_bin_svelte_kit: string; export const npm_package_dependencies_sirv: string; export const npm_package_dependencies_sade: string; export const npm_package_dependencies_mrmime: string; export const npm_package_dependencies_magic_string: string; export const npm_config_version_commit_hooks: string; - export const npm_config_user_agent: string; - export const CI: string; export const npm_package_scripts_generate_version: string; export const npm_package_dependencies__types_cookie: string; export const npm_config_bin_links: string; - export const RUNNER_ENVIRONMENT: string; - export const GITHUB_ENV: string; - export const PIPX_HOME: string; + export const CHRONOSPHERE_ORG_NAME: string; + export const XDG_SESSION_TYPE: string; + export const GIT_ASKPASS: string; export const npm_node_execpath: string; export const npm_package_devDependencies_vite: string; export const npm_package_devDependencies__sveltejs_vite_plugin_svelte: string; export const npm_config_init_version: string; - export const JAVA_HOME_8_X64: string; export const SHLVL: string; + export const BROWSER: string; + export const npm_config_noproxy: string; export const npm_package_exports___node_types: string; export const npm_package_files_0: string; + export const GO15VENDOREXPERIMENT: string; export const HOME: string; export const npm_package_files_1: string; export const npm_package_repository_directory: string; - export const RUNNER_TEMP: string; - export const GITHUB_EVENT_PATH: string; + export const TERM_PROGRAM_VERSION: string; + export const VSCODE_IPC_HOOK_CLI: string; + export const npm_package_json: string; export const npm_package_files_2: string; - export const JAVA_HOME_11_X64: string; - export const PIPX_BIN_DIR: string; - export const GITHUB_REPOSITORY_OWNER: string; + export const PYENV_SHELL: string; + export const BASH_DEFAULT_TIMEOUT_MS: string; export const npm_package_engines_node: string; export const npm_package_exports___vite_import: string; export const npm_package_files_3: string; export const npm_package_devDependencies_svelte_preprocess: string; export const npm_config_init_license: string; - export const GRADLE_HOME: string; - export const ANDROID_NDK_LATEST_HOME: string; - export const JAVA_HOME_21_X64: string; - export const STATS_RDCL: string; - export const GITHUB_RETENTION_DAYS: string; + export const NODE_OPTIONS: string; + export const VSCODE_GIT_ASKPASS_MAIN: string; export const npm_package_files_4: string; export const npm_config_version_tag_prefix: string; - export const GITHUB_REPOSITORY_OWNER_ID: string; - export const POWERSHELL_DISTRIBUTION_CHANNEL: string; - export const AZURE_EXTENSION_DIR: string; - export const GITHUB_HEAD_REF: string; + export const VSCODE_GIT_ASKPASS_NODE: string; + export const SSL_CERT_FILE: string; + export const npm_config_userconfig: string; + export const npm_config_local_prefix: string; export const npm_package_scripts_check: string; export const npm_package_files_5: string; export const npm_package_dependencies_tiny_glob: string; - export const SYSTEMD_EXEC_PID: string; + export const NEW_MACHINE: string; + export const GH_NO_UPDATE_NOTIFIER: string; + export const DBUS_SESSION_BUS_ADDRESS: string; export const npm_package_scripts_postinstall: string; export const npm_package_files_6: string; - export const GITHUB_GRAPHQL_URL: string; + export const COLORTERM: string; + export const GOOGLE_APPLICATION_CREDENTIALS: string; + export const COLOR: string; export const npm_package_devDependencies_typescript: string; export const npm_package_devDependencies__types_connect: string; export const npm_package_description: string; - export const GOROOT_1_20_X64: string; - export const NVM_DIR: string; + export const DEBUGINFOD_URLS: string; export const npm_package_readmeFilename: string; export const npm_package_types: string; export const npm_package_homepage: string; - export const DOTNET_SKIP_FIRST_TIME_EXPERIENCE: string; - export const GOROOT_1_21_X64: string; - export const JAVA_HOME_17_X64: string; - export const ImageVersion: string; + export const USE_GKE_GCLOUD_AUTH_PLUGIN: string; export const npm_package_exports___hooks_types: string; export const npm_package_devDependencies__playwright_test: string; - export const RUNNER_OS: string; - export const GITHUB_API_URL: string; - export const GOROOT_1_22_X64: string; - export const SWIFT_PATH: string; + export const LOGNAME: string; export const npm_package_type: string; - export const RUNNER_USER: string; - export const STATS_V3PS: string; - export const CHROMEWEBDRIVER: string; - export const JOURNAL_STREAM: string; - export const GITHUB_WORKFLOW: string; + export const CLAUDE_CODE_USE_VERTEX: string; export const _: string; + export const npm_config_prefix: string; + export const npm_config_npm_version: string; + export const XDG_SESSION_CLASS: string; export const npm_package_scripts_lint: string; export const npm_config_registry: string; - export const ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE: string; - export const STATS_D: string; - export const GITHUB_RUN_ID: string; - export const STATS_VMFE: string; - export const GITHUB_REF_TYPE: string; - export const BOOTSTRAP_HASKELL_NONINTERACTIVE: string; - export const GITHUB_WORKFLOW_SHA: string; - export const GITHUB_BASE_REF: string; - export const ImageOS: string; + export const TERM: string; + export const XDG_SESSION_ID: string; + export const npm_config_cache: string; + export const CLOUD_ML_REGION: string; export const npm_package_exports___import: string; export const npm_package_devDependencies_dts_buddy: string; export const npm_package_dependencies_kleur: string; export const npm_package_dependencies_devalue: string; export const npm_config_ignore_scripts: string; - export const GITHUB_WORKFLOW_REF: string; - export const PERFLOG_LOCATION_SETTING: string; - export const GITHUB_ACTION_REPOSITORY: string; + export const npm_config_node_gyp: string; export const npm_package_exports___package_json: string; export const npm_package_peerDependencies_svelte: string; export const PATH: string; export const NODE: string; - export const ANT_HOME: string; - export const DOTNET_MULTILEVEL_LOOKUP: string; - export const RUNNER_TRACKING_ID: string; - export const INVOCATION_ID: string; - export const RUNNER_TOOL_CACHE: string; - export const npm_package_repository_type: string; export const npm_package_name: string; - export const GITHUB_ACTION: string; - export const GITHUB_RUN_NUMBER: string; - export const GITHUB_TRIGGERING_ACTOR: string; - export const RUNNER_ARCH: string; + export const npm_package_repository_type: string; export const XDG_RUNTIME_DIR: string; - export const AGENT_TOOLSDIRECTORY: string; export const npm_package_scripts_test_integration: string; export const npm_package_exports___node_polyfills_import: string; export const npm_package_devDependencies__types_set_cookie_parser: string; + export const DEVBOX: string; + export const SSL_CERT_DIR: string; export const npm_package_scripts_test_unit: string; export const npm_package_exports___vite_types: string; export const npm_config_ignore_path: string; export const LANG: string; - export const VCPKG_INSTALLATION_ROOT: string; - export const CONDA: string; - export const RUNNER_NAME: string; - export const XDG_CONFIG_HOME: string; - export const STATS_VMD: string; - export const GITHUB_REF_NAME: string; - export const GITHUB_REPOSITORY: string; - export const STATS_D_D: string; + export const LS_COLORS: string; + export const VSCODE_GIT_IPC_HANDLE: string; + export const TERM_PROGRAM: string; export const npm_lifecycle_script: string; export const npm_package_scripts_test_cross_platform_dev: string; - export const STATS_UE: string; - export const ANDROID_NDK_ROOT: string; - export const GITHUB_ACTION_REF: string; - export const DEBIAN_FRONTEND: string; + export const SSH_AUTH_SOCK: string; export const npm_package_scripts_test: string; export const npm_package_dependencies_esm_env: string; export const npm_config_version_git_message: string; - export const GITHUB_REPOSITORY_ID: string; - export const GITHUB_ACTIONS: string; + export const SHELL: string; + export const GITHUB_PERSONAL_ACCESS_TOKEN: string; + export const GOPATH: string; export const npm_lifecycle_event: string; export const npm_package_repository_url: string; export const npm_package_version: string; - export const GITHUB_REF_PROTECTED: string; + export const PYENV_VIRTUALENV_DISABLE_PROMPT: string; export const npm_config_argv: string; export const npm_package_scripts_generate_types: string; export const npm_package_scripts_check_all: string; export const npm_package_devDependencies_svelte: string; export const npm_package_dependencies_cookie: string; - export const GITHUB_WORKSPACE: string; - export const ACCEPT_EULA: string; - export const GITHUB_JOB: string; - export const RUNNER_PERFLOG: string; + export const LESSCLOSE: string; export const YARN_IGNORE_PATH: string; export const npm_package_exports___node_import: string; - export const GITHUB_SHA: string; - export const GITHUB_RUN_ATTEMPT: string; export const npm_package_devDependencies__types_sade: string; export const npm_config_version_git_tag: string; export const npm_config_version_git_sign: string; - export const GITHUB_REF: string; - export const GITHUB_ACTOR: string; - export const ANDROID_SDK_ROOT: string; export const npm_package_license: string; export const npm_config_strict_ssl: string; - export const LEIN_HOME: string; + export const VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; + export const npm_config_globalconfig: string; + export const npm_config_init_module: string; export const npm_package_scripts_format: string; - export const GITHUB_PATH: string; - export const JAVA_HOME: string; export const PWD: string; - export const GITHUB_ACTOR_ID: string; - export const RUNNER_WORKSPACE: string; + export const GCP_DEVBOX: string; export const npm_execpath: string; export const npm_package_dependencies_set_cookie_parser: string; - export const HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS: string; - export const STATS_TIS: string; - export const GITHUB_EVENT_NAME: string; - export const HOMEBREW_NO_AUTO_UPDATE: string; - export const ANDROID_HOME: string; - export const GITHUB_SERVER_URL: string; - export const GECKOWEBDRIVER: string; - export const LEIN_JAR: string; - export const GHCUP_INSTALL_BASE_PREFIX: string; - export const GITHUB_OUTPUT: string; + export const VIRTUAL_ENV_DISABLE_PROMPT: string; + export const SSH_CONNECTION: string; + export const PYENV_ROOT: string; + export const XDG_DATA_DIRS: string; + export const npm_config_global_prefix: string; export const npm_package_exports___types: string; - export const EDGEWEBDRIVER: string; - export const STATS_EXT: string; + export const npm_command: string; export const npm_package_peerDependencies_vite: string; export const npm_package_peerDependencies__sveltejs_vite_plugin_svelte: string; export const npm_config_save_prefix: string; export const npm_config_ignore_optional: string; - export const ANDROID_NDK: string; - export const SGX_AESM_ADDR: string; - export const CHROME_BIN: string; - export const SELENIUM_JAR_PATH: string; - export const STATS_EXTP: string; + export const MANPATH: string; export const npm_package_exports___node_polyfills_types: string; export const INIT_CWD: string; - export const ANDROID_NDK_HOME: string; - export const GITHUB_STEP_SUMMARY: string; + export const EDITOR: string; export const npm_package_exports___hooks_import: string; export const npm_package_dependencies_import_meta_resolve: string; } @@ -270,217 +222,169 @@ declare module '$env/static/public' { */ declare module '$env/dynamic/private' { export const env: { - GITHUB_STATE: string; - GIT_CLONE_PROTECTION_ACTIVE: string; + PYENV_VIRTUALENV_INIT: string; + ANTHROPIC_VERTEX_PROJECT_ID: string; + LESSOPEN: string; npm_package_scripts_test_cross_platform_build: string; npm_package_devDependencies_rollup: string; npm_package_devDependencies__types_node: string; - STATS_TRP: string; - DEPLOYMENT_BASEPATH: string; - DOTNET_NOLOGO: string; npm_package_devDependencies_vitest: string; + TFLINT_OPA_POLICY_DIR: string; USER: string; + SSH_CLIENT: string; + npm_config_user_agent: string; npm_package_bin_svelte_kit: string; npm_package_dependencies_sirv: string; npm_package_dependencies_sade: string; npm_package_dependencies_mrmime: string; npm_package_dependencies_magic_string: string; npm_config_version_commit_hooks: string; - npm_config_user_agent: string; - CI: string; npm_package_scripts_generate_version: string; npm_package_dependencies__types_cookie: string; npm_config_bin_links: string; - RUNNER_ENVIRONMENT: string; - GITHUB_ENV: string; - PIPX_HOME: string; + CHRONOSPHERE_ORG_NAME: string; + XDG_SESSION_TYPE: string; + GIT_ASKPASS: string; npm_node_execpath: string; npm_package_devDependencies_vite: string; npm_package_devDependencies__sveltejs_vite_plugin_svelte: string; npm_config_init_version: string; - JAVA_HOME_8_X64: string; SHLVL: string; + BROWSER: string; + npm_config_noproxy: string; npm_package_exports___node_types: string; npm_package_files_0: string; + GO15VENDOREXPERIMENT: string; HOME: string; npm_package_files_1: string; npm_package_repository_directory: string; - RUNNER_TEMP: string; - GITHUB_EVENT_PATH: string; + TERM_PROGRAM_VERSION: string; + VSCODE_IPC_HOOK_CLI: string; + npm_package_json: string; npm_package_files_2: string; - JAVA_HOME_11_X64: string; - PIPX_BIN_DIR: string; - GITHUB_REPOSITORY_OWNER: string; + PYENV_SHELL: string; + BASH_DEFAULT_TIMEOUT_MS: string; npm_package_engines_node: string; npm_package_exports___vite_import: string; npm_package_files_3: string; npm_package_devDependencies_svelte_preprocess: string; npm_config_init_license: string; - GRADLE_HOME: string; - ANDROID_NDK_LATEST_HOME: string; - JAVA_HOME_21_X64: string; - STATS_RDCL: string; - GITHUB_RETENTION_DAYS: string; + NODE_OPTIONS: string; + VSCODE_GIT_ASKPASS_MAIN: string; npm_package_files_4: string; npm_config_version_tag_prefix: string; - GITHUB_REPOSITORY_OWNER_ID: string; - POWERSHELL_DISTRIBUTION_CHANNEL: string; - AZURE_EXTENSION_DIR: string; - GITHUB_HEAD_REF: string; + VSCODE_GIT_ASKPASS_NODE: string; + SSL_CERT_FILE: string; + npm_config_userconfig: string; + npm_config_local_prefix: string; npm_package_scripts_check: string; npm_package_files_5: string; npm_package_dependencies_tiny_glob: string; - SYSTEMD_EXEC_PID: string; + NEW_MACHINE: string; + GH_NO_UPDATE_NOTIFIER: string; + DBUS_SESSION_BUS_ADDRESS: string; npm_package_scripts_postinstall: string; npm_package_files_6: string; - GITHUB_GRAPHQL_URL: string; + COLORTERM: string; + GOOGLE_APPLICATION_CREDENTIALS: string; + COLOR: string; npm_package_devDependencies_typescript: string; npm_package_devDependencies__types_connect: string; npm_package_description: string; - GOROOT_1_20_X64: string; - NVM_DIR: string; + DEBUGINFOD_URLS: string; npm_package_readmeFilename: string; npm_package_types: string; npm_package_homepage: string; - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: string; - GOROOT_1_21_X64: string; - JAVA_HOME_17_X64: string; - ImageVersion: string; + USE_GKE_GCLOUD_AUTH_PLUGIN: string; npm_package_exports___hooks_types: string; npm_package_devDependencies__playwright_test: string; - RUNNER_OS: string; - GITHUB_API_URL: string; - GOROOT_1_22_X64: string; - SWIFT_PATH: string; + LOGNAME: string; npm_package_type: string; - RUNNER_USER: string; - STATS_V3PS: string; - CHROMEWEBDRIVER: string; - JOURNAL_STREAM: string; - GITHUB_WORKFLOW: string; + CLAUDE_CODE_USE_VERTEX: string; _: string; + npm_config_prefix: string; + npm_config_npm_version: string; + XDG_SESSION_CLASS: string; npm_package_scripts_lint: string; npm_config_registry: string; - ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE: string; - STATS_D: string; - GITHUB_RUN_ID: string; - STATS_VMFE: string; - GITHUB_REF_TYPE: string; - BOOTSTRAP_HASKELL_NONINTERACTIVE: string; - GITHUB_WORKFLOW_SHA: string; - GITHUB_BASE_REF: string; - ImageOS: string; + TERM: string; + XDG_SESSION_ID: string; + npm_config_cache: string; + CLOUD_ML_REGION: string; npm_package_exports___import: string; npm_package_devDependencies_dts_buddy: string; npm_package_dependencies_kleur: string; npm_package_dependencies_devalue: string; npm_config_ignore_scripts: string; - GITHUB_WORKFLOW_REF: string; - PERFLOG_LOCATION_SETTING: string; - GITHUB_ACTION_REPOSITORY: string; + npm_config_node_gyp: string; npm_package_exports___package_json: string; npm_package_peerDependencies_svelte: string; PATH: string; NODE: string; - ANT_HOME: string; - DOTNET_MULTILEVEL_LOOKUP: string; - RUNNER_TRACKING_ID: string; - INVOCATION_ID: string; - RUNNER_TOOL_CACHE: string; - npm_package_repository_type: string; npm_package_name: string; - GITHUB_ACTION: string; - GITHUB_RUN_NUMBER: string; - GITHUB_TRIGGERING_ACTOR: string; - RUNNER_ARCH: string; + npm_package_repository_type: string; XDG_RUNTIME_DIR: string; - AGENT_TOOLSDIRECTORY: string; npm_package_scripts_test_integration: string; npm_package_exports___node_polyfills_import: string; npm_package_devDependencies__types_set_cookie_parser: string; + DEVBOX: string; + SSL_CERT_DIR: string; npm_package_scripts_test_unit: string; npm_package_exports___vite_types: string; npm_config_ignore_path: string; LANG: string; - VCPKG_INSTALLATION_ROOT: string; - CONDA: string; - RUNNER_NAME: string; - XDG_CONFIG_HOME: string; - STATS_VMD: string; - GITHUB_REF_NAME: string; - GITHUB_REPOSITORY: string; - STATS_D_D: string; + LS_COLORS: string; + VSCODE_GIT_IPC_HANDLE: string; + TERM_PROGRAM: string; npm_lifecycle_script: string; npm_package_scripts_test_cross_platform_dev: string; - STATS_UE: string; - ANDROID_NDK_ROOT: string; - GITHUB_ACTION_REF: string; - DEBIAN_FRONTEND: string; + SSH_AUTH_SOCK: string; npm_package_scripts_test: string; npm_package_dependencies_esm_env: string; npm_config_version_git_message: string; - GITHUB_REPOSITORY_ID: string; - GITHUB_ACTIONS: string; + SHELL: string; + GITHUB_PERSONAL_ACCESS_TOKEN: string; + GOPATH: string; npm_lifecycle_event: string; npm_package_repository_url: string; npm_package_version: string; - GITHUB_REF_PROTECTED: string; + PYENV_VIRTUALENV_DISABLE_PROMPT: string; npm_config_argv: string; npm_package_scripts_generate_types: string; npm_package_scripts_check_all: string; npm_package_devDependencies_svelte: string; npm_package_dependencies_cookie: string; - GITHUB_WORKSPACE: string; - ACCEPT_EULA: string; - GITHUB_JOB: string; - RUNNER_PERFLOG: string; + LESSCLOSE: string; YARN_IGNORE_PATH: string; npm_package_exports___node_import: string; - GITHUB_SHA: string; - GITHUB_RUN_ATTEMPT: string; npm_package_devDependencies__types_sade: string; npm_config_version_git_tag: string; npm_config_version_git_sign: string; - GITHUB_REF: string; - GITHUB_ACTOR: string; - ANDROID_SDK_ROOT: string; npm_package_license: string; npm_config_strict_ssl: string; - LEIN_HOME: string; + VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; + npm_config_globalconfig: string; + npm_config_init_module: string; npm_package_scripts_format: string; - GITHUB_PATH: string; - JAVA_HOME: string; PWD: string; - GITHUB_ACTOR_ID: string; - RUNNER_WORKSPACE: string; + GCP_DEVBOX: string; npm_execpath: string; npm_package_dependencies_set_cookie_parser: string; - HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS: string; - STATS_TIS: string; - GITHUB_EVENT_NAME: string; - HOMEBREW_NO_AUTO_UPDATE: string; - ANDROID_HOME: string; - GITHUB_SERVER_URL: string; - GECKOWEBDRIVER: string; - LEIN_JAR: string; - GHCUP_INSTALL_BASE_PREFIX: string; - GITHUB_OUTPUT: string; + VIRTUAL_ENV_DISABLE_PROMPT: string; + SSH_CONNECTION: string; + PYENV_ROOT: string; + XDG_DATA_DIRS: string; + npm_config_global_prefix: string; npm_package_exports___types: string; - EDGEWEBDRIVER: string; - STATS_EXT: string; + npm_command: string; npm_package_peerDependencies_vite: string; npm_package_peerDependencies__sveltejs_vite_plugin_svelte: string; npm_config_save_prefix: string; npm_config_ignore_optional: string; - ANDROID_NDK: string; - SGX_AESM_ADDR: string; - CHROME_BIN: string; - SELENIUM_JAR_PATH: string; - STATS_EXTP: string; + MANPATH: string; npm_package_exports___node_polyfills_types: string; INIT_CWD: string; - ANDROID_NDK_HOME: string; - GITHUB_STEP_SUMMARY: string; + EDITOR: string; npm_package_exports___hooks_import: string; npm_package_dependencies_import_meta_resolve: string; [key: `PUBLIC_${string}`]: undefined; diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index e9e7614bea..f441c0d339 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,8 +1,8 @@ { - "name": "rrweb-player", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-player", + "version": "2.0.0-alpha.18.6", "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/package": "^2.0.0", @@ -20,8 +20,8 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "@rrweb/replay": "^2.0.0-alpha.18", - "@rrweb/packer": "^2.0.0-alpha.18" + "@mixpanel/rrweb-replay": "^2.0.0-alpha.18", + "@mixpanel/rrweb-packer": "^2.0.0-alpha.18" }, "scripts": { "dev": "vite build --watch", diff --git a/packages/rrweb-player/tsconfig.node.json b/packages/rrweb-player/tsconfig.node.json index d02c37dfe5..c0a1eb092a 100644 --- a/packages/rrweb-player/tsconfig.node.json +++ b/packages/rrweb-player/tsconfig.node.json @@ -4,7 +4,7 @@ "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "strict": true + "strict": false }, - "include": ["vite.config.ts"] + "include": ["vite.config.ts"], } diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 299384b2de..7ba5c9944e 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { - "name": "rrweb-snapshot", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-snapshot", + "version": "2.0.0-alpha.18.6", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", @@ -12,7 +12,7 @@ "test:update": "yarn build && vitest run --update", "bench": "vite build && vitest bench", "dev": "vite build --watch", - "build": "yarn turbo prepublish -F rrweb-snapshot", + "build": "yarn turbo prepublish -F @mixpanel/rrweb-snapshot", "check-types": "tsc --noEmit", "prepublish": "yarn check-types && vite build", "lint": "yarn eslint src" @@ -20,7 +20,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb", @@ -50,12 +50,12 @@ "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-snapshot#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/master/packages/rrweb-snapshot#readme", "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.18", - "@rrweb/utils": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-utils": "^2.0.0-alpha.18", "@types/jsdom": "^20.0.0", "@types/node": "^18.15.11", "@types/puppeteer": "^5.4.4", diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 8c29fa0d7f..1028d065b4 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -638,6 +638,19 @@ function serializeElementNode( attributes.checked = checked; } } + if ( + (tagName === 'input' || tagName === 'textarea') && + attributes.placeholder + ) { + attributes.placeholder = maskInputValue({ + element: n, + type: getInputType(n), + tagName, + value: attributes.placeholder as string, + maskInputOptions, + maskInputFn, + }); + } if (tagName === 'option') { if ((n as HTMLOptionElement).selected && !maskInputOptions['select']) { attributes.selected = true; @@ -1283,6 +1296,7 @@ function snapshot( textarea: true, select: true, password: true, + hidden: true, } : maskAllInputs === false ? { diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 83b50e266f..9bdf561378 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -47,6 +47,7 @@ export type MaskInputOptions = Partial<{ textarea: boolean; select: boolean; password: boolean; + hidden: boolean; }>; export type SlimDOMOptions = Partial<{ diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 102787c666..4f1ef7f1d8 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -203,14 +203,15 @@ export class Mirror implements IMirror { } // removes the node from idNodeMap - // doesn't remove the node from nodeMetaMap - removeNodeFromMap(n: Node) { + // if permanent is true, also removes from nodeMetaMap + removeNodeFromMap(n: Node, permanent = false) { const id = this.getId(n); this.idNodeMap.delete(id); + if (permanent) this.nodeMetaMap.delete(n); if (n.childNodes) { n.childNodes.forEach((childNode) => - this.removeNodeFromMap(childNode as unknown as Node), + this.removeNodeFromMap(childNode as unknown as Node, permanent), ); } } diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts index 0a82d8c16c..156bac353e 100644 --- a/packages/rrweb-snapshot/test/utils.test.ts +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -7,6 +7,7 @@ import { extractFileExtension, fixSafariColons, isNodeMetaEqual, + Mirror, } from '../src/utils'; import { NodeType } from '@rrweb/types'; import type { serializedNode, serializedNodeWithId } from '@rrweb/types'; @@ -280,4 +281,130 @@ describe('utils', () => { expect(out3).toEqual('[data-aa\\:other] { color: red; }'); }); }); + + describe('Mirror', () => { + describe('removeNodeFromMap with permanent flag', () => { + it('should remove node from both idNodeMap and nodeMetaMap', () => { + const mirror = new Mirror(); + const div = document.createElement('div'); + const meta = { + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 1, + } as serializedNodeWithId; + + mirror.add(div, meta); + expect(mirror.getId(div)).toBe(1); + expect(mirror.hasNode(div)).toBe(true); + expect(mirror.getNode(1)).toBe(div); + + mirror.removeNodeFromMap(div, true); + + expect(mirror.getId(div)).toBe(-1); + expect(mirror.hasNode(div)).toBe(false); + expect(mirror.getNode(1)).toBeNull(); + }); + + it('should recursively remove child nodes', () => { + const mirror = new Mirror(); + + const parent = document.createElement('div'); + const child1 = document.createElement('span'); + const child2 = document.createElement('p'); + const grandchild = document.createElement('a'); + + parent.appendChild(child1); + parent.appendChild(child2); + child1.appendChild(grandchild); + + const parentMeta = { + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 1, + } as serializedNodeWithId; + const child1Meta = { + type: NodeType.Element, + tagName: 'span', + attributes: {}, + childNodes: [], + id: 2, + } as serializedNodeWithId; + const child2Meta = { + type: NodeType.Element, + tagName: 'p', + attributes: {}, + childNodes: [], + id: 3, + } as serializedNodeWithId; + const grandchildMeta = { + type: NodeType.Element, + tagName: 'a', + attributes: {}, + childNodes: [], + id: 4, + } as serializedNodeWithId; + + mirror.add(parent, parentMeta); + mirror.add(child1, child1Meta); + mirror.add(child2, child2Meta); + mirror.add(grandchild, grandchildMeta); + + expect(mirror.hasNode(parent)).toBe(true); + expect(mirror.hasNode(child1)).toBe(true); + expect(mirror.hasNode(child2)).toBe(true); + expect(mirror.hasNode(grandchild)).toBe(true); + + mirror.removeNodeFromMap(parent, true); + + expect(mirror.hasNode(parent)).toBe(false); + expect(mirror.hasNode(child1)).toBe(false); + expect(mirror.hasNode(child2)).toBe(false); + expect(mirror.hasNode(grandchild)).toBe(false); + expect(mirror.getId(parent)).toBe(-1); + expect(mirror.getId(child1)).toBe(-1); + expect(mirror.getId(child2)).toBe(-1); + expect(mirror.getId(grandchild)).toBe(-1); + }); + + it('should differ from non-permanent removeNodeFromMap by also clearing nodeMetaMap', () => { + const mirror1 = new Mirror(); + const div1 = document.createElement('div'); + const meta1 = { + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 1, + } as serializedNodeWithId; + + mirror1.add(div1, meta1); + mirror1.removeNodeFromMap(div1); + + // removeNodeFromMap only clears idNodeMap, not nodeMetaMap + expect(mirror1.getNode(1)).toBeNull(); + expect(mirror1.hasNode(div1)).toBe(true); // Still in nodeMetaMap + + const mirror2 = new Mirror(); + const div2 = document.createElement('div'); + const meta2 = { + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 1, + } as serializedNodeWithId; + + mirror2.add(div2, meta2); + mirror2.removeNodeFromMap(div2, true); + + // removeNodeFromMap with permanent=true clears both maps + expect(mirror2.getNode(1)).toBeNull(); + expect(mirror2.hasNode(div2)).toBe(false); // Also removed from nodeMetaMap + }); + }); + }); }); diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index abb46b258e..fa928ca496 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { - "name": "rrweb", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb", + "version": "2.0.0-alpha.18.6", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -26,7 +26,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+ssh://git@github.com/rrweb-io/rrweb.git" + "url": "git+ssh://git@github.com/mixpanel/rrweb.git" }, "keywords": [ "rrweb" @@ -56,9 +56,9 @@ "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, - "homepage": "https://github.com/rrweb-io/rrweb#readme", + "homepage": "https://github.com/mixpanel/rrweb#readme", "devDependencies": { "@types/dom-mediacapture-transform": "0.1.4", "@types/inquirer": "^8.2.1", @@ -80,13 +80,13 @@ "vite-plugin-dts": "^3.9.1" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.18", - "@rrweb/utils": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-utils": "^2.0.0-alpha.18", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "mitt": "^3.0.0", - "rrdom": "^2.0.0-alpha.18", - "rrweb-snapshot": "^2.0.0-alpha.18" + "@mixpanel/rrdom": "^2.0.0-alpha.18", + "@mixpanel/rrweb-snapshot": "^2.0.0-alpha.18" } } diff --git a/packages/rrweb/src/record/cross-origin-utils.ts b/packages/rrweb/src/record/cross-origin-utils.ts new file mode 100644 index 0000000000..00f17c8f81 --- /dev/null +++ b/packages/rrweb/src/record/cross-origin-utils.ts @@ -0,0 +1,32 @@ +function toOrigin(url: string): string | null { + try { + const origin = new URL(url).origin; + return origin !== 'null' ? origin : null; + } catch { + return null; + } +} + +export function buildAllowedOriginSet(origins: string[]): ReadonlySet { + if (!Array.isArray(origins) || origins.length === 0) { + throw new Error( + '[rrweb] allowedIframeOrigins must be a non-empty array of origin strings.', + ); + } + + const set = new Set(); + for (let i = 0; i < origins.length; i++) { + const entry = origins[i]; + if (typeof entry !== 'string') { + throw new Error( + `[rrweb] allowedIframeOrigins[${i}] must be a string, got ${typeof entry}.`, + ); + } + const origin = toOrigin(entry); + if (origin) { + set.add(origin); + } + } + + return Object.freeze(set); +} diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 0451743619..8bcd6f401b 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -19,6 +19,10 @@ export class IframeManager { public crossOriginIframeStyleMirror: CrossOriginIframeMirror; public crossOriginIframeRootIdMap: WeakMap = new WeakMap(); + private iframeContentDocumentMap: WeakMap = + new WeakMap(); + private iframeObserverCleanupMap: WeakMap void> = + new WeakMap(); private mirror: Mirror; private mutationCb: mutationCallBack; private wrappedEmit: (e: eventWithoutTime, isCheckout?: boolean) => void; @@ -54,6 +58,47 @@ export class IframeManager { this.crossOriginIframeMap.set(iframeEl.contentWindow, iframeEl); } + public getIframeContentDocument( + iframeEl: HTMLIFrameElement, + ): Document | undefined { + return this.iframeContentDocumentMap.get(iframeEl); + } + + public setObserverCleanup( + iframeEl: HTMLIFrameElement, + cleanup: () => void, + ): void { + this.iframeObserverCleanupMap.set(iframeEl, cleanup); + } + + public getObserverCleanup( + iframeEl: HTMLIFrameElement, + ): (() => void) | undefined { + return this.iframeObserverCleanupMap.get(iframeEl); + } + + public removeIframe(iframeEl: HTMLIFrameElement): void { + const storedDoc = this.iframeContentDocumentMap.get(iframeEl); + + if (storedDoc) { + this.stylesheetManager.cleanupStylesheetsForRemovedNode(storedDoc); + this.mirror.removeNodeFromMap(storedDoc, true); + } + + this.iframes.delete(iframeEl); + this.iframeContentDocumentMap.delete(iframeEl); + + const observerCleanup = this.iframeObserverCleanupMap.get(iframeEl); + if (observerCleanup) { + try { + observerCleanup(); + } catch (e) { + // Ignore errors during cleanup + } + this.iframeObserverCleanupMap.delete(iframeEl); + } + } + public addLoadListener(cb: (iframeEl: HTMLIFrameElement) => unknown) { this.loadListener = cb; } @@ -76,6 +121,10 @@ export class IframeManager { isAttachIframe: true, }); + if (iframeEl.contentDocument) { + this.iframeContentDocumentMap.set(iframeEl, iframeEl.contentDocument); + } + // Receive messages (events) coming from cross-origin iframes that are nested in this same-origin iframe. if (this.recordCrossOriginIframes) iframeEl.contentWindow?.addEventListener( diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 65da8ec801..f8f0982b67 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -4,7 +4,11 @@ import { type SlimDOMOptions, createMirror, } from 'rrweb-snapshot'; -import { initObservers, mutationBuffers } from './observer'; +import { + initObservers, + mutationBuffers, + removeMutationBufferForDoc, +} from './observer'; import { on, getWindowWidth, @@ -39,6 +43,7 @@ import { registerErrorHandler, unregisterErrorHandler, } from './error-handler'; +import { buildAllowedOriginSet } from './cross-origin-utils'; import dom from '@rrweb/utils'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; @@ -89,6 +94,7 @@ function record( recordDOM = true, recordCanvas = false, recordCrossOriginIframes = false, + allowedIframeOrigins, recordAfter = options.recordAfter === 'DOMContentLoaded' ? options.recordAfter : 'load', @@ -103,6 +109,18 @@ function record( registerErrorHandler(errorHandler); + let validatedOrigins: ReadonlySet | undefined; + if ( + recordCrossOriginIframes && + allowedIframeOrigins && + allowedIframeOrigins.length > 0 + ) { + validatedOrigins = buildAllowedOriginSet(allowedIframeOrigins); + if (validatedOrigins.size === 0) { + validatedOrigins = undefined; + } + } + const inEmittingFrame = recordCrossOriginIframes ? window.parent === window : true; @@ -155,6 +173,7 @@ function record( textarea: true, select: true, password: true, + hidden: true, } : _maskInputOptions !== undefined ? _maskInputOptions @@ -226,7 +245,13 @@ function record( origin: window.location.origin, isCheckout, }; - window.parent.postMessage(message, '*'); + if (validatedOrigins) { + for (const targetOrigin of validatedOrigins) { + window.parent.postMessage(message, targetOrigin); + } + } else { + window.parent.postMessage(message, '*'); + } } if (e.type === EventType.FullSnapshot) { @@ -575,7 +600,28 @@ function record( iframeManager.addLoadListener((iframeEl) => { try { - handlers.push(observe(iframeEl.contentDocument!)); + const iframeDoc = iframeEl.contentDocument!; + const iframeHandler = observe(iframeDoc); + handlers.push(iframeHandler); + + const existingCleanup = iframeManager.getObserverCleanup(iframeEl); + iframeManager.setObserverCleanup(iframeEl, () => { + if (existingCleanup) { + try { + existingCleanup(); + } catch (e) { + // Ignore errors during cleanup + } + } + try { + iframeHandler(); + const idx = handlers.indexOf(iframeHandler); + if (idx !== -1) handlers.splice(idx, 1); + removeMutationBufferForDoc(iframeDoc); + } catch (e) { + // Ignore errors during cleanup + } + }); } catch (error) { // TODO: handle internal error console.warn(error); @@ -625,9 +671,9 @@ function record( /** * https://github.com/rrweb-io/rrweb/pull/1695 * This error can occur in a known scenario: - * If an iframe is initially same-origin and observed, but later its - location is changed in an opaque way to a cross-origin URL (perhaps within the iframe via its `document.location` or a redirect) - * attempting to execute the handler in the stop record function will + * If an iframe is initially same-origin and observed, but later its + location is changed in an opaque way to a cross-origin URL (perhaps within the iframe via its `document.location` or a redirect) + * attempting to execute the handler in the stop record function will throw a "cannot access cross-origin frame" error. * This error is expected and can be safely ignored. */ diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 08e927a98f..58369129da 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -256,6 +256,10 @@ export default class MutationBuffer { this.canvasManager.reset(); } + public getDoc(): Document | undefined { + return this.doc; + } + public processMutations = (mutations: mutationRecord[]) => { mutations.forEach(this.processMutation); // adds mutations to the buffer this.emit(); // clears buffer if not locked/frozen @@ -363,7 +367,9 @@ export default class MutationBuffer { }; while (this.mapRemoves.length) { - this.mirror.removeNodeFromMap(this.mapRemoves.shift()!); + const removedNode = this.mapRemoves.shift()!; + this.cleanupRemovedNode(removedNode); + this.mirror.removeNodeFromMap(removedNode); } for (const n of this.movedSet) { @@ -581,7 +587,7 @@ export default class MutationBuffer { let attributeName = m.attributeName as string; let value = (m.target as HTMLElement).getAttribute(attributeName); - if (attributeName === 'value') { + if (attributeName === 'value' || attributeName === 'placeholder') { const type = getInputType(target); value = maskInputValue({ @@ -801,6 +807,26 @@ export default class MutationBuffer { } } }; + + private cleanupRemovedNode = (node: Node) => { + if (node.nodeName === 'IFRAME') { + try { + this.iframeManager.removeIframe(node as HTMLIFrameElement); + } catch (e) { + // Ignore errors during iframe cleanup + } + } else { + try { + this.stylesheetManager.cleanupStylesheetsForRemovedNode(node); + } catch (e) { + // Ignore errors during stylesheet cleanup + } + } + + node.childNodes.forEach((child) => { + this.cleanupRemovedNode(child); + }); + }; } /** diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 8326d79651..23bb433644 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -102,6 +102,15 @@ export function initMutationObserver( return observer; } +export function removeMutationBufferForDoc(doc: Document): void { + for (let i = mutationBuffers.length - 1; i >= 0; i--) { + const buffer = mutationBuffers[i]; + if (buffer.getDoc() === doc) { + mutationBuffers.splice(i, 1); + } + } +} + function initMoveObserver({ mousemoveCb, sampling, diff --git a/packages/rrweb/src/record/stylesheet-manager.ts b/packages/rrweb/src/record/stylesheet-manager.ts index 8656c09c59..6d49c5beab 100644 --- a/packages/rrweb/src/record/stylesheet-manager.ts +++ b/packages/rrweb/src/record/stylesheet-manager.ts @@ -84,6 +84,49 @@ export class StylesheetManager { this.trackedLinkElements = new WeakSet(); } + /** + * Cleans up stylesheets associated with a removed node. + * + * @param removedNode - The node that was removed from the DOM. + */ + public cleanupStylesheetsForRemovedNode(removedNode: Node): void { + try { + if (removedNode.nodeType === Node.DOCUMENT_NODE) { + const doc = removedNode as Document; + if (doc.adoptedStyleSheets) { + for (const sheet of doc.adoptedStyleSheets) { + this.styleMirror.remove(sheet); + } + } + } + + if (removedNode.nodeName === 'STYLE') { + const styleEl = removedNode as HTMLStyleElement; + if (styleEl.sheet) { + this.styleMirror.remove(styleEl.sheet); + } + } + + if ( + removedNode.nodeName === 'LINK' && + (removedNode as HTMLLinkElement).rel === 'stylesheet' + ) { + const linkEl = removedNode as HTMLLinkElement; + if (linkEl.sheet) { + this.styleMirror.remove(linkEl.sheet); + } + } + + if (removedNode.childNodes) { + removedNode.childNodes.forEach((child) => { + this.cleanupStylesheetsForRemovedNode(child); + }); + } + } catch (e) { + // Ignore errors + } + } + // TODO: take snapshot on stylesheet reload by applying event listener private trackStylesheetInLinkElement(_linkEl: HTMLLinkElement) { // linkEl.addEventListener('load', () => { diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 5f1101cc23..cecd84b471 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1302,6 +1302,9 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } + if (!this.mediaManager.isSupportedMediaElement(target)) { + return this.debugNodeNotFound(d, d.id); + } const mediaEl = target as HTMLMediaElement | RRMediaElement; const { events } = this.service.state.context; @@ -2048,11 +2051,13 @@ export class Replayer { styleSheet.rules, data.index, ) as unknown as CSSStyleRule; - rule.style.setProperty( - data.set.property, - data.set.value, - data.set.priority, - ); + if (rule?.style) { + rule.style.setProperty( + data.set.property, + data.set.value, + data.set.priority, + ); + } } if (data.remove) { @@ -2060,7 +2065,9 @@ export class Replayer { styleSheet.rules, data.index, ) as unknown as CSSStyleRule; - rule.style.removeProperty(data.remove.property); + if (rule?.style) { + rule.style.removeProperty(data.remove.property); + } } } diff --git a/packages/rrweb/src/replay/media/index.ts b/packages/rrweb/src/replay/media/index.ts index ef577cafa0..01e1b5e080 100644 --- a/packages/rrweb/src/replay/media/index.ts +++ b/packages/rrweb/src/replay/media/index.ts @@ -285,7 +285,7 @@ export class MediaManager { this.syncTargetWithState(target); } - public isSupportedMediaElement(node: Node): node is HTMLMediaElement { + public isSupportedMediaElement(node: Node | { nodeName: string }): boolean { return ['AUDIO', 'VIDEO'].includes(node.nodeName); } diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index a03e326b6f..fe77240c68 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -65,6 +65,7 @@ export type recordOptions = { recordDOM?: boolean; recordCanvas?: boolean; recordCrossOriginIframes?: boolean; + allowedIframeOrigins?: string[]; recordAfter?: 'DOMContentLoaded' | 'load'; userTriggeredOnInput?: boolean; collectFonts?: boolean; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 13b15ec6c1..e68b1eb83a 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -501,6 +501,16 @@ export class StyleSheetMirror { generateId(): number { return this.id++; } + + remove(stylesheet: CSSStyleSheet): boolean { + const id = this.styleIDMap.get(stylesheet); + if (id !== undefined) { + this.styleIDMap.delete(stylesheet); + this.idStyleMap.delete(id); + return true; + } + return false; + } } /** diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 738f2fe8e2..b6a4fa887b 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -2665,7 +2665,8 @@ exports[`record integration tests > can record form interactions 1`] = ` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"Enter your email\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -2806,7 +2807,8 @@ exports[`record integration tests > can record form interactions 1`] = ` \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -2952,8 +2954,23 @@ exports[`record integration tests > can record form interactions 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"123456\\" + }, + \\"childNodes\\": [], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 65 } ], \\"id\\": 18 @@ -2961,7 +2978,7 @@ exports[`record integration tests > can record form interactions 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 64 + \\"id\\": 66 }, { \\"type\\": 2, @@ -2971,15 +2988,15 @@ exports[`record integration tests > can record form interactions 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 66 + \\"id\\": 68 } ], - \\"id\\": 65 + \\"id\\": 67 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 67 + \\"id\\": 69 } ], \\"id\\": 16 @@ -4649,7 +4666,8 @@ exports[`record integration tests > can use maskInputOptions to configure which \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"Enter your email\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -4790,7 +4808,8 @@ exports[`record integration tests > can use maskInputOptions to configure which \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -4936,8 +4955,23 @@ exports[`record integration tests > can use maskInputOptions to configure which }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"123456\\" + }, + \\"childNodes\\": [], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 65 } ], \\"id\\": 18 @@ -4945,7 +4979,7 @@ exports[`record integration tests > can use maskInputOptions to configure which { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 64 + \\"id\\": 66 }, { \\"type\\": 2, @@ -4955,15 +4989,15 @@ exports[`record integration tests > can use maskInputOptions to configure which { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 66 + \\"id\\": 68 } ], - \\"id\\": 65 + \\"id\\": 67 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 67 + \\"id\\": 69 } ], \\"id\\": 16 @@ -6477,7 +6511,8 @@ exports[`record integration tests > should mask inputs via function call 1`] = ` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"****************\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -6618,7 +6653,8 @@ exports[`record integration tests > should mask inputs via function call 1`] = ` \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -6763,8 +6799,23 @@ exports[`record integration tests > should mask inputs via function call 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"******\\" + }, + \\"childNodes\\": [], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 65 } ], \\"id\\": 18 @@ -6772,7 +6823,7 @@ exports[`record integration tests > should mask inputs via function call 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 64 + \\"id\\": 66 }, { \\"type\\": 2, @@ -6782,15 +6833,15 @@ exports[`record integration tests > should mask inputs via function call 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 66 + \\"id\\": 68 } ], - \\"id\\": 65 + \\"id\\": 67 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 67 + \\"id\\": 69 } ], \\"id\\": 16 @@ -10305,7 +10356,8 @@ exports[`record integration tests > should not record input values if maskAllInp \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"****************\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -10446,7 +10498,8 @@ exports[`record integration tests > should not record input values if maskAllInp \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"**********************\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -10591,8 +10644,23 @@ exports[`record integration tests > should not record input values if maskAllInp }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"******\\" + }, + \\"childNodes\\": [], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 65 } ], \\"id\\": 18 @@ -10600,7 +10668,7 @@ exports[`record integration tests > should not record input values if maskAllInp { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 64 + \\"id\\": 66 }, { \\"type\\": 2, @@ -10610,15 +10678,15 @@ exports[`record integration tests > should not record input values if maskAllInp { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 66 + \\"id\\": 68 } ], - \\"id\\": 65 + \\"id\\": 67 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 67 + \\"id\\": 69 } ], \\"id\\": 16 @@ -13418,7 +13486,8 @@ exports[`record integration tests > should record input userTriggered values if \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"Enter your email\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -13559,7 +13628,8 @@ exports[`record integration tests > should record input userTriggered values if \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -13705,8 +13775,23 @@ exports[`record integration tests > should record input userTriggered values if }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"123456\\" + }, + \\"childNodes\\": [], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 65 } ], \\"id\\": 18 @@ -13714,7 +13799,7 @@ exports[`record integration tests > should record input userTriggered values if { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 64 + \\"id\\": 66 }, { \\"type\\": 2, @@ -13724,15 +13809,15 @@ exports[`record integration tests > should record input userTriggered values if { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 66 + \\"id\\": 68 } ], - \\"id\\": 65 + \\"id\\": 67 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 67 + \\"id\\": 69 } ], \\"id\\": 16 diff --git a/packages/rrweb/test/html/form.html b/packages/rrweb/test/html/form.html index 8480259fe0..d930e106bc 100644 --- a/packages/rrweb/test/html/form.html +++ b/packages/rrweb/test/html/form.html @@ -10,7 +10,7 @@
+
diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 89534c99c7..2e719e8480 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -14,7 +14,12 @@ import { ISuite, } from './utils'; import type { recordOptions } from '../src/types'; -import { eventWithTime, NodeType, EventType } from '@rrweb/types'; +import { + eventWithTime, + NodeType, + EventType, + serializedNodeWithId, +} from '@rrweb/types'; import { visitSnapshot } from 'rrweb-snapshot'; describe('record integration tests', function (this: ISuite) { @@ -617,6 +622,43 @@ describe('record integration tests', function (this: ISuite) { await assertSnapshot(snapshots); }); + it('should mask placeholder values if maskAllInputs is enabled', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml.call(this, 'form.html', { maskAllInputs: true }), + ); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + + const fullSnapshot = snapshots.find( + (s) => s.type === EventType.FullSnapshot, + ); + expect(fullSnapshot).toBeDefined(); + + let foundMaskedPlaceholder = false; + visitSnapshot( + (fullSnapshot as eventWithTime & { data: { node: serializedNodeWithId } }) + .data.node, + (node) => { + if ( + node.type === NodeType.Element && + (node.tagName === 'input' || node.tagName === 'textarea') && + node.attributes.placeholder + ) { + const placeholder = node.attributes.placeholder as string; + expect(placeholder).not.toContain('Enter your email'); + expect(placeholder).not.toContain('Tell us about yourself'); + expect(placeholder).toMatch(/^\*+$/); + foundMaskedPlaceholder = true; + } + }, + ); + expect(foundMaskedPlaceholder).toBe(true); + }); + it('should record input userTriggered values if userTriggeredOnInput is enabled', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); diff --git a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap index 8cd9fce959..2621e7d408 100644 --- a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap +++ b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap @@ -1109,7 +1109,8 @@ exports[`cross origin iframes > form.html > should map input events correctly 1` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"Enter your email\\" }, \\"childNodes\\": [], \\"rootId\\": 11, @@ -1270,7 +1271,8 @@ exports[`cross origin iframes > form.html > should map input events correctly 1` \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"rootId\\": 11, @@ -1438,9 +1440,26 @@ exports[`cross origin iframes > form.html > should map input events correctly 1` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"rootId\\": 11, \\"id\\": 75 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"123456\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 76 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, + \\"id\\": 77 } ], \\"rootId\\": 11, @@ -1450,7 +1469,7 @@ exports[`cross origin iframes > form.html > should map input events correctly 1` \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", \\"rootId\\": 11, - \\"id\\": 76 + \\"id\\": 78 } ], \\"rootId\\": 11, @@ -2122,7 +2141,8 @@ exports[`cross origin iframes > form.html > should map scroll events correctly 1 \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"placeholder\\": \\"Enter your email\\" }, \\"childNodes\\": [], \\"rootId\\": 11, @@ -2283,7 +2303,8 @@ exports[`cross origin iframes > form.html > should map scroll events correctly 1 \\"id\\": \\"\\", \\"cols\\": \\"30\\", \\"rows\\": \\"10\\", - \\"data-unmask-example\\": \\"true\\" + \\"data-unmask-example\\": \\"true\\", + \\"placeholder\\": \\"Tell us about yourself\\" }, \\"childNodes\\": [], \\"rootId\\": 11, @@ -2451,9 +2472,26 @@ exports[`cross origin iframes > form.html > should map scroll events correctly 1 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"rootId\\": 11, \\"id\\": 75 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"hidden\\", + \\"value\\": \\"123456\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 76 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, + \\"id\\": 77 } ], \\"rootId\\": 11, @@ -2463,7 +2501,7 @@ exports[`cross origin iframes > form.html > should map scroll events correctly 1 \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", \\"rootId\\": 11, - \\"id\\": 76 + \\"id\\": 78 } ], \\"rootId\\": 11, diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index cd6738fa78..01ec84d693 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -40,11 +40,13 @@ interface IWindow extends Window { } type ExtraOptions = { usePackFn?: boolean; + crossOrigin?: boolean; }; async function injectRecordScript( frame: puppeteer.Frame, options?: ExtraOptions, + allowedOrigins?: string[], ) { try { await frame.addScriptTag({ @@ -58,26 +60,31 @@ async function injectRecordScript( !e.message.includes('DOM.describeNode') ) throw e; - await injectRecordScript(frame, options); + await injectRecordScript(frame, options, allowedOrigins); return; } options = options || {}; - await frame.evaluate((options) => { - (window as unknown as IWindow).snapshots = []; - const { record } = (window as unknown as IWindow).rrweb; - const config: recordOptions = { - recordCrossOriginIframes: true, - recordCanvas: true, - emit(event) { - (window as unknown as IWindow).snapshots.push(event); - (window as unknown as IWindow).emit(event); - }, - }; - record(config); - }, options); + await frame.evaluate( + (options, allowedOrigins) => { + (window as unknown as IWindow).snapshots = []; + const { record } = (window as unknown as IWindow).rrweb; + const config: recordOptions = { + recordCrossOriginIframes: !!allowedOrigins, + recordCanvas: true, + allowedIframeOrigins: allowedOrigins, + emit(event) { + (window as unknown as IWindow).snapshots.push(event); + (window as unknown as IWindow).emit(event); + }, + }; + record(config); + }, + options, + allowedOrigins, + ); for (const child of frame.childFrames()) { - await injectRecordScript(child, options); + await injectRecordScript(child, options, allowedOrigins); } } @@ -104,11 +111,10 @@ const setup = function ( beforeEach(async () => { ctx.page = await ctx.browser.newPage(); - await ctx.page.goto('about:blank'); + await ctx.page.goto(`${ctx.serverURL}/html/blank.html`); await ctx.page.setContent( - content.replace(/\{SERVER_URL\}/g, ctx.serverURL), + content.replace(/\{SERVER_URL\}/g, ctx.serverBURL), ); - // await ctx.page.evaluate(ctx.code); ctx.events = []; await ctx.page.exposeFunction('emit', (e: eventWithTime) => { if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) { @@ -118,7 +124,10 @@ const setup = function ( }); ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); - await injectRecordScript(ctx.page.mainFrame(), options); + const allowedOrigins = options?.crossOrigin + ? [ctx.serverURL, ctx.serverBURL] + : undefined; + await injectRecordScript(ctx.page.mainFrame(), options, allowedOrigins); }); afterEach(async () => { @@ -138,7 +147,7 @@ describe('cross origin iframes', function (this: ISuite) { vi.setConfig({ testTimeout: 100_000 }); describe('form.html', function (this: ISuite) { - const ctx: ISuite = setup.call( + const ctx = setup.call( this, ` @@ -148,7 +157,8 @@ describe('cross origin iframes', function (this: ISuite) { `, - ); + { crossOrigin: true }, + ) as ISuite & { serverBURL: string }; it("won't emit events if it isn't in the top level iframe", async () => { const el = (await ctx.page.$( @@ -214,12 +224,16 @@ describe('cross origin iframes', function (this: ISuite) { await ctx.page.evaluate((url) => { const iframe = document.querySelector('iframe') as HTMLIFrameElement; iframe.src = `${url}/html/empty.html`; - }, ctx.serverURL); + }, ctx.serverBURL); await waitForRAF(ctx.page); // should load iframe (but sometimes doesn't) const frame = ctx.page.mainFrame().childFrames()[0]; await frame.waitForSelector('#one'); // ensure frame has changed - await injectRecordScript(ctx.page.mainFrame().childFrames()[0]); // injects script into new iframe + await injectRecordScript( + ctx.page.mainFrame().childFrames()[0], + undefined, + [ctx.serverURL, ctx.serverBURL], + ); // injects script into new iframe const events: eventWithTime[] = await ctx.page.evaluate( () => (window as unknown as IWindow).snapshots, @@ -277,7 +291,7 @@ describe('cross origin iframes', function (this: ISuite) { }); describe('move-node.html', function (this: ISuite) { - const ctx: ISuite = setup.call( + const ctx = setup.call( this, ` @@ -287,7 +301,8 @@ describe('cross origin iframes', function (this: ISuite) { `, - ); + { crossOrigin: true }, + ) as ISuite & { serverBURL: string }; it('should record DOM node movement', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; @@ -494,7 +509,7 @@ describe('cross origin iframes', function (this: ISuite) { describe('audio.html', function (this: ISuite) { vi.setConfig({ testTimeout: 100_000 }); - const ctx: ISuite = setup.call( + const ctx = setup.call( this, ` @@ -504,7 +519,8 @@ describe('cross origin iframes', function (this: ISuite) { `, - ); + { crossOrigin: true }, + ) as ISuite & { serverBURL: string }; it('should emit contents of iframe once', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; @@ -529,7 +545,7 @@ describe('cross origin iframes', function (this: ISuite) { `; - const ctx = setup.call(this, content) as ISuite & { + const ctx = setup.call(this, content, { crossOrigin: true }) as ISuite & { serverBURL: string; }; @@ -553,7 +569,7 @@ describe('cross origin iframes', function (this: ISuite) { it('should filter out forwarded cross origin rrweb messages', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; - const iframe2URL = `${ctx.serverBURL}/html/blank.html`; + const iframe2URL = `${ctx.serverURL}/html/blank.html?iframe2`; await frame.evaluate((iframe2URL) => { // Add a message proxy to forward messages from child frames to its parent frame. window.addEventListener('message', (event) => { @@ -569,9 +585,16 @@ describe('cross origin iframes', function (this: ISuite) { await ctx.page.waitForFrame(iframe2URL); const iframe2 = frame.childFrames()[0]; // Record iframe2 - await injectRecordScript(iframe2); + await injectRecordScript(iframe2, undefined, [ + ctx.serverURL, + ctx.serverBURL, + ]); + // Wait for the 2-hop postMessage chain (iframe2 → iframe1 → parent). + // Each frame has its own event loop, so wait on each in order. await waitForRAF(iframe2); + await waitForRAF(frame); + await waitForRAF(ctx.page); const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; @@ -583,7 +606,7 @@ describe('cross origin iframes', function (this: ISuite) { describe('same origin iframes', function (this: ISuite) { vi.setConfig({ testTimeout: 100_000 }); - const ctx: ISuite = setup.call( + const ctx = setup.call( this, ` @@ -593,7 +616,8 @@ describe('same origin iframes', function (this: ISuite) { `, - ); + { crossOrigin: true }, + ) as ISuite & { serverBURL: string }; it('should emit contents of iframe once', async () => { const events = await ctx.page.evaluate( @@ -618,10 +642,13 @@ describe('same origin iframes', function (this: ISuite) { return new Promise((resolve) => { crossOriginIframe.onload = resolve; }); - }, ctx.serverURL); + }, ctx.serverBURL); const crossOriginIframe = sameOriginIframe.childFrames()[0]; // Inject recording script into this cross-origin iframe - await injectRecordScript(crossOriginIframe); + await injectRecordScript(crossOriginIframe, undefined, [ + ctx.serverURL, + ctx.serverBURL, + ]); await waitForRAF(ctx.page); const snapshots = (await ctx.page.evaluate( diff --git a/packages/rrweb/test/record/cross-origin-utils.test.ts b/packages/rrweb/test/record/cross-origin-utils.test.ts new file mode 100644 index 0000000000..43be818cfd --- /dev/null +++ b/packages/rrweb/test/record/cross-origin-utils.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect } from 'vitest'; +import { buildAllowedOriginSet } from '../../src/record/cross-origin-utils'; + +describe('buildAllowedOriginSet', () => { + it('should return a frozen Set for valid origins', () => { + const result = buildAllowedOriginSet([ + 'https://example.com', + 'https://app.example.com', + ]); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(2); + expect(result.has('https://example.com')).toBe(true); + expect(result.has('https://app.example.com')).toBe(true); + expect(Object.isFrozen(result)).toBe(true); + }); + + it('should normalize URLs to their origins', () => { + const result = buildAllowedOriginSet([ + 'https://example.com/', + 'https://example.com/path/to/page', + 'https://app.example.com?foo=bar', + 'http://localhost:3000#hash', + ]); + expect(result.size).toBe(3); + expect(result.has('https://example.com')).toBe(true); + expect(result.has('https://app.example.com')).toBe(true); + expect(result.has('http://localhost:3000')).toBe(true); + }); + + it('should normalize default ports away', () => { + const result = buildAllowedOriginSet([ + 'https://example.com:443', + 'http://example.com:80', + ]); + expect(result.size).toBe(2); + expect(result.has('https://example.com')).toBe(true); + expect(result.has('http://example.com')).toBe(true); + }); + + it('should throw for empty array', () => { + expect(() => buildAllowedOriginSet([])).toThrow( + 'allowedIframeOrigins must be a non-empty array', + ); + }); + + it('should throw for non-array', () => { + expect(() => buildAllowedOriginSet(null as unknown as string[])).toThrow( + 'allowedIframeOrigins must be a non-empty array', + ); + expect(() => + buildAllowedOriginSet(undefined as unknown as string[]), + ).toThrow('allowedIframeOrigins must be a non-empty array'); + }); + + it('should throw for non-string entries', () => { + expect(() => buildAllowedOriginSet([123 as unknown as string])).toThrow( + 'must be a string', + ); + }); + + it('should skip unparseable strings', () => { + const result = buildAllowedOriginSet(['https://example.com', 'not-a-url']); + expect(result.size).toBe(1); + expect(result.has('https://example.com')).toBe(true); + }); + + it('should deduplicate origins', () => { + const result = buildAllowedOriginSet([ + 'https://example.com', + 'https://example.com', + ]); + expect(result.size).toBe(1); + }); + + it('should deduplicate after normalization', () => { + const result = buildAllowedOriginSet([ + 'https://example.com', + 'https://example.com/path', + 'https://example.com:443', + ]); + expect(result.size).toBe(1); + expect(result.has('https://example.com')).toBe(true); + }); +}); diff --git a/packages/rrweb/test/util.test.ts b/packages/rrweb/test/util.test.ts index fda0030b67..801e425e8b 100644 --- a/packages/rrweb/test/util.test.ts +++ b/packages/rrweb/test/util.test.ts @@ -80,6 +80,36 @@ describe('Utilities for other modules', () => { for (let i = 0; i < 10; i++) expect(mirror.getStyle(i + 1)).toBeNull(); expect(mirror.add(new CSSStyleSheet())).toBe(1); }); + + it('can remove CSSStyleSheet from the mirror', () => { + const mirror = new StyleSheetMirror(); + const styleSheet = new CSSStyleSheet(); + mirror.add(styleSheet); + expect(mirror.has(styleSheet)).toBeTruthy(); + expect(mirror.remove(styleSheet)).toBe(true); + expect(mirror.has(styleSheet)).toBeFalsy(); + expect(mirror.getId(styleSheet)).toBe(-1); + }); + + it('returns false when removing a non-existent stylesheet', () => { + const mirror = new StyleSheetMirror(); + const styleSheet = new CSSStyleSheet(); + expect(mirror.remove(styleSheet)).toBe(false); + }); + + it('does not affect other stylesheets when removing one', () => { + const mirror = new StyleSheetMirror(); + const styleSheet1 = new CSSStyleSheet(); + const styleSheet2 = new CSSStyleSheet(); + mirror.add(styleSheet1); + mirror.add(styleSheet2); + const id2 = mirror.getId(styleSheet2); + mirror.remove(styleSheet1); + expect(mirror.has(styleSheet1)).toBeFalsy(); + expect(mirror.has(styleSheet2)).toBeTruthy(); + expect(mirror.getId(styleSheet2)).toBe(id2); + expect(mirror.getStyle(id2)).toBe(styleSheet2); + }); }); describe('inDom()', () => { diff --git a/packages/types/package.json b/packages/types/package.json index 8fce976d2e..a5c24b72ab 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/types", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-types", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -15,29 +15,29 @@ "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/types#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/types.umd.cjs", - "module": "./dist/types.js", - "unpkg": "./dist/types.umd.cjs", + "main": "./dist/rrweb-types.umd.cjs", + "module": "./dist/rrweb-types.js", + "unpkg": "./dist/rrweb-types.umd.cjs", "typings": "dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/types.js" + "default": "./dist/rrweb-types.js" }, "require": { "types": "./dist/index.d.cts", - "default": "./dist/types.umd.cjs" + "default": "./dist/rrweb-types.umd.cjs" } } }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index bba276e483..384d1cd4f3 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -828,7 +828,7 @@ export interface IMirror { getMeta(n: TNode): serializedNodeWithId | null; - removeNodeFromMap(n: TNode): void; + removeNodeFromMap(n: TNode, permanent?: boolean): void; has(id: number): boolean; diff --git a/packages/utils/package.json b/packages/utils/package.json index 10f0141da1..4001a565dc 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { - "name": "@rrweb/utils", - "version": "2.0.0-alpha.18", + "name": "@mixpanel/rrweb-utils", + "version": "2.0.0-alpha.18.6", "publishConfig": { "access": "public" }, @@ -15,29 +15,29 @@ "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/utils#readme", + "homepage": "https://github.com/mixpanel/rrweb/tree/main/packages/@rrweb/utils#readme", "bugs": { - "url": "https://github.com/rrweb-io/rrweb/issues" + "url": "https://github.com/mixpanel/rrweb/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/rrweb-io/rrweb.git" + "url": "git+https://github.com/mixpanel/rrweb.git" }, "license": "MIT", "type": "module", - "main": "./dist/utils.umd.cjs", - "module": "./dist/utils.js", - "unpkg": "./dist/utils.umd.cjs", + "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/utils.js" + "default": "./dist/rrweb-utils.js" }, "require": { "types": "./dist/index.d.cts", - "default": "./dist/utils.umd.cjs" + "default": "./dist/rrweb-utils.umd.cjs" } } }, diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index f9f340a53c..6efe88447b 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { - "name": "@rrweb/web-extension", + "name": "@mixpanel/rrweb-web-extension", "private": true, - "version": "2.0.0-alpha.18", + "version": "2.0.0-alpha.18.6", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -15,10 +15,10 @@ "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" + "prepublish": "echo 'Skipping build for extension'" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.18", + "@mixpanel/rrweb-types": "^2.0.0-alpha.18", "@types/chrome": "^0.0.287", "@types/react-dom": "^18.0.6", "@types/semver": "^7.5.8", @@ -44,7 +44,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.18", - "rrweb-player": "^2.0.0-alpha.18" + "@mixpanel/rrweb": "^2.0.0-alpha.18", + "@mixpanel/rrweb-player": "^2.0.0-alpha.18" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index bcee1c7337..6519e50b7c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -8,6 +8,27 @@ "target": "ESNext", "module": "ESNext", "moduleResolution": "Node", + "baseUrl": ".", + "paths": { + "@rrweb/types": ["./packages/types"], + "@rrweb/utils": ["./packages/utils"], + "@rrweb/packer": ["./packages/packer"], + "@rrweb/record": ["./packages/record"], + "@rrweb/replay": ["./packages/replay"], + "@rrweb/all": ["./packages/all"], + "@rrweb/rrweb-plugin-console-record": ["./packages/plugins/rrweb-plugin-console-record"], + "@rrweb/rrweb-plugin-console-replay": ["./packages/plugins/rrweb-plugin-console-replay"], + "@rrweb/rrweb-plugin-sequential-id-record": ["./packages/plugins/rrweb-plugin-sequential-id-record"], + "@rrweb/rrweb-plugin-sequential-id-replay": ["./packages/plugins/rrweb-plugin-sequential-id-replay"], + "@rrweb/rrweb-plugin-canvas-webrtc-record": ["./packages/plugins/rrweb-plugin-canvas-webrtc-record"], + "@rrweb/rrweb-plugin-canvas-webrtc-replay": ["./packages/plugins/rrweb-plugin-canvas-webrtc-replay"], + "rrweb": ["./packages/rrweb"], + "rrweb-snapshot": ["./packages/rrweb-snapshot"], + "rrdom": ["./packages/rrdom"], + "rrdom-nodejs": ["./packages/rrdom-nodejs"], + "rrweb-player": ["./packages/rrweb-player"], + "rrvideo": ["./packages/rrvideo"], + }, "rootDir": "src", "outDir": "dist", "lib": ["es6", "dom"], diff --git a/vite.config.default.ts b/vite.config.default.ts index 46af9327a6..bd95b5a77e 100644 --- a/vite.config.default.ts +++ b/vite.config.default.ts @@ -146,10 +146,53 @@ export default function ( // }, // }, }, + + resolve: { + alias: { + // Map @rrweb scoped packages to the one in this repo so we don't have to update source imports to reference @mixpanel + '@rrweb/types': resolve(__dirname, './packages/types'), + '@rrweb/utils': resolve(__dirname, './packages/utils'), + '@rrweb/packer': resolve(__dirname, './packages/packer'), + '@rrweb/record': resolve(__dirname, './packages/record'), + '@rrweb/replay': resolve(__dirname, './packages/replay'), + '@rrweb/all': resolve(__dirname, './packages/all'), + '@rrweb/rrweb-plugin-console-record': resolve( + __dirname, + './packages/plugins/rrweb-plugin-console-record', + ), + '@rrweb/rrweb-plugin-console-replay': resolve( + __dirname, + './packages/plugins/rrweb-plugin-console-replay', + ), + '@rrweb/rrweb-plugin-sequential-id-record': resolve( + __dirname, + './packages/plugins/rrweb-plugin-sequential-id-record', + ), + '@rrweb/rrweb-plugin-sequential-id-replay': resolve( + __dirname, + './packages/plugins/rrweb-plugin-sequential-id-replay', + ), + '@rrweb/rrweb-plugin-canvas-webrtc-record': resolve( + __dirname, + './packages/plugins/rrweb-plugin-canvas-webrtc-record', + ), + '@rrweb/rrweb-plugin-canvas-webrtc-replay': resolve( + __dirname, + './packages/plugins/rrweb-plugin-canvas-webrtc-replay', + ), + rrweb: resolve(__dirname, './packages/rrweb'), + 'rrweb-snapshot': resolve(__dirname, './packages/rrweb-snapshot'), + rrdom: resolve(__dirname, './packages/rrdom'), + 'rrdom-nodejs': resolve(__dirname, './packages/rrdom-nodejs'), + 'rrweb-player': resolve(__dirname, './packages/rrweb-player'), + rrvideo: resolve(__dirname, './packages/rrvideo'), + }, + }, + plugins: [ dts({ insertTypesEntry: true, - rollupTypes: true, + rollupTypes: false, afterBuild: (emittedFiles: Map) => { // To pass publint (`npm x publint@latest`) and ensure the // package is supported by all consumers, we must export types that are diff --git a/write-mixpanel-version.js b/write-mixpanel-version.js new file mode 100644 index 0000000000..b022ccaa45 --- /dev/null +++ b/write-mixpanel-version.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node +// working around the changesets setup because we want to be able to build and deploy mixpanel's own version of rrweb +// while keeping the core business logic intact. mixpanel doesn't need the changesets, +// so we'll just add an extra "mixpanel patch" number to indicate it's different than the rrweb-io version +// e.g. +// 2.0.0-alpha.18.1 means we have changes that are not in rrweb 2.0.0-alpha.18 +// 2.0.0-alpha.19 means we are 1:1 with upstream rrweb 2.0.0-alpha.19 + +const fs = require('fs'); +const path = require('path'); + +function findPackageJsonFiles(dir, results = []) { + try { + const items = fs.readdirSync(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + + if (item.isDirectory() && item.name !== 'node_modules') { + findPackageJsonFiles(fullPath, results); + } else if (item.isFile() && item.name === 'package.json') { + results.push(fullPath); + } + } + } catch (error) { + // Skip directories we can't read + } + + return results; +} + +function bumpVersion(version) { + const versionNum = version.split('alpha.')[1].split('.'); + if (versionNum.length === 1) { + return `${version}.1`; // If no mixpanel patch, add .1 + } + const patchNumber = parseInt(versionNum[1], 10); + return `2.0.0-alpha.${versionNum[0]}.${patchNumber + 1}`; +} + +function processPackageJson(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8'); + const packageJson = JSON.parse(content); + + if (packageJson.version) { + const oldVersion = packageJson.version; + packageJson.version = bumpVersion(packageJson.version); + console.log(`${path.relative(process.cwd(), filePath)}: ${oldVersion} → ${packageJson.version}`); + + const updatedContent = JSON.stringify(packageJson, null, 2) + '\n'; + fs.writeFileSync(filePath, updatedContent, 'utf8'); + } + } catch (error) { + console.error(`Error processing ${filePath}: ${error.message}`); + } +} + +function main() { + const args = process.argv.slice(2); + + if (args.includes('--help') || args.includes('-h')) { + console.log(` +Usage: node bump-version.js + +Bumps version numbers in all package.json files in the packages directory. +Adds .1 to versions without mixpanel patches, increments existing patches. + +Examples: + 2.0.0-alpha.18 → 2.0.0-alpha.18.1 + 2.0.0-alpha.18.1 → 2.0.0-alpha.18.2 + `); + return; + } + + const packagesDir = path.join(__dirname, 'packages'); + + if (!fs.existsSync(packagesDir)) { + console.error('Error: packages directory not found'); + return; + } + + const packageJsonFiles = findPackageJsonFiles(packagesDir); + + if (packageJsonFiles.length === 0) { + console.log('No package.json files found'); + return; + } + + console.log(`Bumping versions in ${packageJsonFiles.length} packages:\n`); + + packageJsonFiles.forEach(processPackageJson); + + console.log('\nDone!'); +} + +// Run the script +main(); diff --git a/yarn.lock b/yarn.lock index 7d0b8c00de..8799603a80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,5 +1,5 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 +# yarn lockfile v1 "@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.2.1": @@ -2375,6 +2375,32 @@ 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/packer@^2.0.0-alpha.18": + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/@rrweb/packer/-/packer-2.0.0-alpha.18.tgz#a529be0cf802b2a2c81db595f6933c389d91b0a2" + integrity sha512-rEXltE/gnflEv/NatVNPIp4/6EtVlPLSwzIbd4WPSMtQGvwZe5OfAAk/Y88HkbAwb1nHfdA3PtFGn2Epik4inQ== + dependencies: + "@rrweb/types" "^2.0.0-alpha.18" + fflate "^0.4.4" + +"@rrweb/replay@^2.0.0-alpha.18": + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/@rrweb/replay/-/replay-2.0.0-alpha.18.tgz#8cd2a02e05f3f5036b7ebd0fd491ddb35a35a144" + integrity sha512-EGCdydiQHCEbh1j+EF7OupwRHD1/EnAYYF4ENBz4CReoH+jGsLKu0LT2962CWPnQttMvpxwzhmD5gFGf63Gm0g== + dependencies: + "@rrweb/types" "^2.0.0-alpha.18" + rrweb "^2.0.0-alpha.18" + +"@rrweb/types@^2.0.0-alpha.18": + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/@rrweb/types/-/types-2.0.0-alpha.18.tgz#e1d9af844cebbf30a2be8808f6cf64f5df3e7f50" + integrity sha512-iMH3amHthJZ9x3gGmBPmdfim7wLGygC2GciIkw2A6SO8giSn8PHYtRT8OKNH4V+k3SZ6RSnYHcTQxBA7pSWZ3Q== + +"@rrweb/utils@^2.0.0-alpha.18": + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/@rrweb/utils/-/utils-2.0.0-alpha.18.tgz#7440b425461cf92b8ad9a229db40fa58d456159a" + integrity sha512-qV8azQYo9RuwW4NGRtOiQfTBdHNL1B0Q//uRLMbCSjbaKqJYd88Js17Bdskj65a0Vgp2dwTLPIZ0gK47dfjfaA== + "@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" @@ -8906,6 +8932,43 @@ rollup@^4.13.0: "@rollup/rollup-win32-x64-msvc" "4.18.0" fsevents "~2.3.2" +rrdom@^2.0.0-alpha.18: + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/rrdom/-/rrdom-2.0.0-alpha.18.tgz#54726a87053c420ef67b7597a31fef515e372e85" + integrity sha512-fSFzFFxbqAViITyYVA4Z0o5G6p1nEqEr/N8vdgSKie9Rn0FJxDSNJgjV0yiCIzcDs0QR+hpvgFhpbdZ6JIr5Nw== + dependencies: + rrweb-snapshot "^2.0.0-alpha.18" + +rrweb-player@^2.0.0-alpha.18: + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/rrweb-player/-/rrweb-player-2.0.0-alpha.18.tgz#c7db140c695c935247dc5aae7f4fd7de6b6fa512" + integrity sha512-v31QgtjR9Ei3JnyvaN/2THj6a/i+dG49LzHiUtHcfTIFi7ecVExmMq+QDctVxUsuwdPPTtEcslpQh2tbVbi/ow== + dependencies: + "@rrweb/packer" "^2.0.0-alpha.18" + "@rrweb/replay" "^2.0.0-alpha.18" + "@tsconfig/svelte" "^1.0.0" + +rrweb-snapshot@^2.0.0-alpha.18: + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.18.tgz#b242d079cb07acadd389a56674465a466b111e20" + integrity sha512-hBHZL/NfgQX6wO1D9mpwqFu1NJPpim+moIcKhFEjVTZVRUfCln+LOugRc4teVTCISYHN8Cw5e2iNTWCSm+SkoA== + dependencies: + postcss "^8.4.38" + +rrweb@^2.0.0-alpha.18: + version "2.0.0-alpha.18" + resolved "https://registry.yarnpkg.com/rrweb/-/rrweb-2.0.0-alpha.18.tgz#19d96bccba44dc1ee37d0b77b9ca1952682e62b5" + integrity sha512-1mjZcB+LVoGSx1+i9E2ZdAP90fS3MghYVix2wvGlZvrgRuLCbTCCOZMztFCkKpgp7/EeCdYM4nIHJkKX5J1Nmg== + dependencies: + "@rrweb/types" "^2.0.0-alpha.18" + "@rrweb/utils" "^2.0.0-alpha.18" + "@types/css-font-loading-module" "0.0.7" + "@xstate/fsm" "^1.4.0" + base64-arraybuffer "^1.0.1" + mitt "^3.0.0" + rrdom "^2.0.0-alpha.18" + rrweb-snapshot "^2.0.0-alpha.18" + run-async@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad"