diff --git a/.changeset/config.json b/.changeset/config.json
index 89ad04bbc7..027c996e1e 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -22,7 +22,8 @@
"@rrweb/rrweb-plugin-sequential-id-record",
"@rrweb/rrweb-plugin-sequential-id-replay",
"@rrweb/rrweb-plugin-canvas-webrtc-record",
- "@rrweb/rrweb-plugin-canvas-webrtc-replay"
+ "@rrweb/rrweb-plugin-canvas-webrtc-replay",
+ "@rrweb/cutter"
]
],
"linked": [],
diff --git a/.changeset/gorgeous-eyes-agree.md b/.changeset/gorgeous-eyes-agree.md
new file mode 100644
index 0000000000..2fd6972ebe
--- /dev/null
+++ b/.changeset/gorgeous-eyes-agree.md
@@ -0,0 +1,8 @@
+---
+"@rrweb/cutter": patch
+---
+
+Feat: Add two tools to process the recording data
+
+1. `session cutter` is a tool to cut the recording data into smaller pieces.
+2. `pruneBranch` is a tool to prune branches from Dom tree, keeping only the nodes of interest.
diff --git a/.changeset/popular-seals-buy.md b/.changeset/popular-seals-buy.md
new file mode 100644
index 0000000000..31833a8219
--- /dev/null
+++ b/.changeset/popular-seals-buy.md
@@ -0,0 +1,5 @@
+---
+"rrweb": patch
+---
+
+Feat: Add a Sync Replayer which can be executed in an RRDom environment.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index cb5885c385..a105564103 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -17,8 +17,11 @@ body:
options:
- rrweb
- rrweb-snapshot
- - rrdom
- rrweb-player
+ - @rrweb/types
+ - rrweb-cutter
+ - rrdom
+ - rrdom-nodejs
- web-extension
- rrvideo
- Other (specify below)
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 0bbb65aa43..fff3d08e97 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -17,8 +17,11 @@ body:
options:
- rrweb
- rrweb-snapshot
- - rrdom
- rrweb-player
+ - @rrweb/types
+ - rrweb-cutter
+ - rrdom
+ - rrdom-nodejs
- web-extension
- rrvideo
- Other (specify below)
diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace
index ecb1672def..1f3886ba71 100644
--- a/.vscode/rrweb-monorepo.code-workspace
+++ b/.vscode/rrweb-monorepo.code-workspace
@@ -76,6 +76,10 @@
{
"name": "@rrweb/rrweb-plugin-canvas-webrtc-replay",
"path": "../packages/plugins/rrweb-plugin-canvas-webrtc-replay"
+ },
+ {
+ "name": "@rrweb/cutter (package)",
+ "path": "../packages/rrweb-cutter"
}
],
"settings": {
diff --git a/packages/rrdom/jest.config.js b/packages/rrdom/jest.config.js
deleted file mode 100644
index 50c5bb3b1b..0000000000
--- a/packages/rrdom/jest.config.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
-export default {
- preset: 'ts-jest',
- testEnvironment: 'node',
- /**
- * Keeps old (pre-jest 29) snapshot format
- * its a bit ugly and harder to read than the new format,
- * so we might want to remove this in its own PR
- */
- snapshotFormat: {
- escapeString: true,
- printBasicPrototype: true,
- },
-};
diff --git a/packages/rrweb-cutter/.eslintignore b/packages/rrweb-cutter/.eslintignore
new file mode 100644
index 0000000000..5a84bb05a1
--- /dev/null
+++ b/packages/rrweb-cutter/.eslintignore
@@ -0,0 +1,3 @@
+vite.config.ts
+jest.config.js
+test
diff --git a/packages/rrweb-cutter/README.md b/packages/rrweb-cutter/README.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/rrweb-cutter/examples/browser/example1.html b/packages/rrweb-cutter/examples/browser/example1.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/rrweb-cutter/examples/server/.gitignore b/packages/rrweb-cutter/examples/server/.gitignore
new file mode 100644
index 0000000000..9678abfa09
--- /dev/null
+++ b/packages/rrweb-cutter/examples/server/.gitignore
@@ -0,0 +1,2 @@
+yarn.lock
+event.json
\ No newline at end of file
diff --git a/packages/rrweb-cutter/examples/server/README.md b/packages/rrweb-cutter/examples/server/README.md
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/rrweb-cutter/examples/server/package.json b/packages/rrweb-cutter/examples/server/package.json
new file mode 100644
index 0000000000..841ca98d7e
--- /dev/null
+++ b/packages/rrweb-cutter/examples/server/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "description": "An example package of using rrweb-cutter with express",
+ "main": "dist/server.js",
+ "scripts": {
+ "start": "node dist/server.js",
+ "build": "tsc",
+ "dev": "nodemon src/index.ts"
+ },
+ "dependencies": {
+ "@rrweb-cutter": "../../",
+ "express": "^4.18.2"
+ },
+ "devDependencies": {
+ "@rrweb/types": "../../../types",
+ "@types/express": "^4.17.17",
+ "nodemon": "^2.0.22",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.7.3"
+ }
+}
diff --git a/packages/rrweb-cutter/examples/server/src/client.html b/packages/rrweb-cutter/examples/server/src/client.html
new file mode 100644
index 0000000000..5f204e5495
--- /dev/null
+++ b/packages/rrweb-cutter/examples/server/src/client.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Replaying rrweb events after time
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/rrweb-cutter/examples/server/src/index.ts b/packages/rrweb-cutter/examples/server/src/index.ts
new file mode 100644
index 0000000000..e3fa6384d7
--- /dev/null
+++ b/packages/rrweb-cutter/examples/server/src/index.ts
@@ -0,0 +1,28 @@
+import * as fs from 'fs';
+import express, { Request, Response } from 'express';
+import { eventWithTime } from '@rrweb/types';
+import { cutSession } from '@rrweb/cutter';
+
+const app = express();
+
+// Sample events load from file.
+// You can replace this with your own events loaded from other places (DataBase, Network, etc).
+const events: eventWithTime[] = JSON.parse(
+ fs.readFileSync('./events1.json').toString(),
+);
+
+app.get('/events/:timepoint', (req: Request, res: Response) => {
+ const timepoint = parseInt(req.params.timepoint);
+ const sessions = cutSession(events, {
+ points: [timepoint],
+ });
+ if (sessions.length < 2)
+ return res.json({
+ error: 'Invalid timepoint',
+ });
+ return res.json(sessions[1].events);
+});
+
+app.listen(3000, () => {
+ console.log('Server started on port 3000');
+});
diff --git a/packages/rrweb-cutter/examples/server/tsconfig.json b/packages/rrweb-cutter/examples/server/tsconfig.json
new file mode 100644
index 0000000000..444f6a1687
--- /dev/null
+++ b/packages/rrweb-cutter/examples/server/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist",
+ "declaration": true,
+ "declarationDir": "dist/types",
+ "target": "ES5",
+ "module": "commonjs",
+ "lib": ["ES6", "DOM"],
+ "moduleResolution": "Node",
+ "sourceMap": true,
+ "strictNullChecks": true,
+ "removeComments": true,
+ "preserveConstEnums": true,
+ "resolveJsonModule": true,
+ "isolatedModules": false,
+ "esModuleInterop": true,
+ "noImplicitReturns": true,
+ "noImplicitAny": true,
+ "skipLibCheck": true
+ },
+ "compileOnSave": true,
+ "include": ["src"],
+ "exclude": ["test"]
+}
diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json
new file mode 100644
index 0000000000..25984eb728
--- /dev/null
+++ b/packages/rrweb-cutter/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "@rrweb/cutter",
+ "version": "2.0.0-alpha.18",
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "dev": "vite build --watch",
+ "build": "yarn turbo run prepublish",
+ "check-types": "tsc -noEmit",
+ "test": "vitest run",
+ "prepublish": "tsc -noEmit && vite build",
+ "lint": "yarn eslint src/**/*.ts",
+ "prune-events": "npx vite-node scripts/prune.ts --"
+ },
+ "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-cutter#README.md",
+ "bugs": {
+ "url": "https://github.com/rrweb-io/rrweb/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rrweb-io/rrweb.git"
+ },
+ "author": "yun.feng0817@gmail.com",
+ "keywords": [
+ "rrweb",
+ "rrdom",
+ "session cutter"
+ ],
+ "license": "MIT",
+ "type": "module",
+ "main": "dist/index.cjs",
+ "unpkg": "dist/index.iife.js",
+ "module": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "files": [
+ "dist",
+ "package.json"
+ ],
+ "devDependencies": {
+ "@types/lodash.clonedeep": "^4.5.9",
+ "typescript": "^5.4.5",
+ "vite": "^5.3.1",
+ "vite-plugin-dts": "^3.9.1"
+ },
+ "dependencies": {
+ "@rrweb/types": "^2.0.0-alpha.18",
+ "lodash.clonedeep": "^4.5.0",
+ "rrdom": "^2.0.0-alpha.18",
+ "rrweb-snapshot": "^2.0.0-alpha.18",
+ "rrweb": "^2.0.0-alpha.18"
+ }
+}
diff --git a/packages/rrweb-cutter/scripts/prune.ts b/packages/rrweb-cutter/scripts/prune.ts
new file mode 100644
index 0000000000..5b1d17c2ab
--- /dev/null
+++ b/packages/rrweb-cutter/scripts/prune.ts
@@ -0,0 +1,13 @@
+import fs from 'fs';
+import { pruneBranches } from '../src';
+
+const myArgs = process.argv.slice(2);
+if (myArgs.length < 3) {
+ console.log('Usage: yarn prune-events