From d17b08fb312b0d6d8e00078ad53e922e274689ef Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 01/52] refactor: eliminate eslint errors as many as I can --- .eslintrc.js | 3 ++ packages/rrweb/package.json | 2 +- packages/rrweb/src/replay/index.ts | 40 ++++++++++++----------- packages/rrweb/src/replay/smoothscroll.ts | 2 +- packages/rrweb/src/utils.ts | 4 +-- packages/rrweb/test/integration.test.ts | 11 +++++-- packages/rrweb/tsconfig.json | 3 +- yarn.lock | 8 ++--- 8 files changed, 42 insertions(+), 31 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2f332b6bbd..ebf67aa9ce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,5 +19,8 @@ module.exports = { plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], rules: { 'tsdoc/syntax': 'warn', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', }, }; diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index c51df94833..322191dd29 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -78,7 +78,7 @@ "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", - "mitt": "^1.1.3", + "mitt": "^3.0.0", "rrdom": "^0.1.2", "rrweb-snapshot": "^1.1.14" } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 99d64b99ca..6695fccf21 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -42,7 +42,6 @@ import { viewportResizeDimension, missingNodeMap, addedNodeMutation, - missingNode, incrementalSnapshotEvent, incrementalData, ReplayerEvents, @@ -54,7 +53,6 @@ import { scrollData, inputData, canvasMutationData, - styleAttributeValue, styleValueWithPriority, mouseMovePos, IWindow, @@ -83,8 +81,7 @@ const SKIP_TIME_THRESHOLD = 10 * 1000; const SKIP_TIME_INTERVAL = 5 * 1000; // https://github.com/rollup/rollup/issues/1267#issuecomment-296395734 -// tslint:disable-next-line -const mitt = (mittProxy as any).default || mittProxy; +const mitt = mittProxy.default || mittProxy; const REPLAY_CONSOLE_PREFIX = '[replayer]'; @@ -188,7 +185,7 @@ export class Replayer { canvasMutationData: canvasMutationData, target: HTMLCanvasElement, ) => { - canvasMutation({ + void canvasMutation({ event: canvasEvent, mutation: canvasMutationData, target, @@ -340,8 +337,10 @@ export class Replayer { public setConfig(config: Partial) { Object.keys(config).forEach((key) => { - // @ts-ignore - this.config[key] = config[key]; + const newConfigValue = config[key as keyof playerConfig]; + (this.config as Record)[ + key as keyof playerConfig + ] = config[key as keyof playerConfig]; }); if (!this.config.skipInactive) { this.backToNormal(); @@ -404,7 +403,7 @@ export class Replayer { * So the implementation of play at any time offset will always iterate * all of the events, cast event before the offset synchronously * and cast event after the offset asynchronously with timer. - * @param timeOffset number + * @param timeOffset - number */ public play(timeOffset = 0) { if (this.service.state.matches('paused')) { @@ -452,7 +451,7 @@ export class Replayer { if (indicatesTouchDevice(event)) { this.mouse.classList.add('touch-device'); } - Promise.resolve().then(() => + void Promise.resolve().then(() => this.service.send({ type: 'ADD_EVENT', payload: { event } }), ); } @@ -714,7 +713,7 @@ export class Replayer { this.waitForStylesheetLoad(); } if (this.config.UNSAFE_replayCanvas) { - this.preloadAllImages(); + void this.preloadAllImages(); } } @@ -875,7 +874,7 @@ export class Replayer { if (!arg || typeof arg !== 'object') { // do nothing } else if ('rr_type' in arg && 'args' in arg) { - if (this.hasImageArg(arg.args)) return true; + if (this.hasImageArg(arg.args as any[])) return true; } else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') { return true; // has image! } else if (arg instanceof Array) { @@ -891,9 +890,9 @@ export class Replayer { if (!arg || typeof arg !== 'object') { // do nothing } else if ('rr_type' in arg && 'args' in arg) { - images.push(...this.getImageArgs(arg.args)); + images.push(...this.getImageArgs(arg.args as any[])); } else if ('rr_type' in arg && arg.rr_type === 'HTMLImageElement') { - images.push(arg.src); + images.push(arg.src as string); } else if (arg instanceof Array) { images.push(...this.getImageArgs(arg)); } @@ -940,7 +939,7 @@ export class Replayer { const ctx = canvas.getContext('2d'); const imgd = ctx?.createImageData(canvas.width, canvas.height); let d = imgd?.data; - d = JSON.parse(data.args[0]); + d = JSON.parse(data.args[0]) as Uint8ClampedArray; ctx?.putImageData(imgd!, 0, 0); } } @@ -1013,6 +1012,7 @@ export class Replayer { }); // add a dummy action to keep timer alive this.timer.addAction({ + // eslint-disable-next-line @typescript-eslint/no-empty-function doAction() {}, delay: e.delay! - d.positions[0]?.timeOffset, }); @@ -1174,7 +1174,7 @@ export class Replayer { // i.e. media will evntualy start to play when data is loaded // 'canplay' event fires even when currentTime attribute changes which may lead to // unexpeted behavior - mediaEl.play(); + void mediaEl.play(); } } catch (error) { if (this.config.showWarning) { @@ -1322,7 +1322,7 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - canvasMutation({ + void canvasMutation({ event: e, mutation: d, target: target as HTMLCanvasElement, @@ -1337,7 +1337,9 @@ export class Replayer { try { const fontFace = new FontFace( d.family, - d.buffer ? new Uint8Array(JSON.parse(d.fontSource)) : d.fontSource, + d.buffer + ? new Uint8Array(JSON.parse(d.fontSource) as Iterable) + : d.fontSource, d.descriptors, ); this.iframe.contentDocument?.fonts.add(fontFace); @@ -1711,8 +1713,8 @@ export class Replayer { /** * Apply the scroll data on real elements. * If the replayer is in sync mode, smooth scroll behavior should be disabled. - * @param d the scroll data - * @param isSync whether the replayer is in sync mode(fast-forward) + * @param d - the scroll data + * @param isSync - whether the replayer is in sync mode(fast-forward) */ private applyScroll(d: scrollData, isSync: boolean) { const target = this.mirror.getNode(d.id); diff --git a/packages/rrweb/src/replay/smoothscroll.ts b/packages/rrweb/src/replay/smoothscroll.ts index 5f03a0aaeb..569968b114 100644 --- a/packages/rrweb/src/replay/smoothscroll.ts +++ b/packages/rrweb/src/replay/smoothscroll.ts @@ -3,8 +3,8 @@ * Add support of customize target window and document */ +/* eslint-disable */ // @ts-nocheck -// tslint:disable export function polyfill(w: Window = window, d = document) { // return if scroll behavior is supported and polyfill is not forced if ( diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index d3c16a8919..0c11195344 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -70,14 +70,14 @@ export function throttle( ) { let timeout: ReturnType | null = null; let previous = 0; - return function (arg: T) { + return function (...args: T[]) { const now = Date.now(); if (!previous && options.leading === false) { previous = now; } const remaining = wait - (now - previous); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-this-alias const context = this; - const args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index fbb23378e5..8caa8cf35c 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -12,7 +12,12 @@ import { generateRecordSnippet, ISuite, } from './utils'; -import { recordOptions, eventWithTime, EventType } from '../src/types'; +import { + recordOptions, + eventWithTime, + EventType, + RecordPlugin, +} from '../src/types'; import { visitSnapshot, NodeType } from 'rrweb-snapshot'; describe('record integration tests', function (this: ISuite) { @@ -442,8 +447,8 @@ describe('record integration tests', function (this: ISuite) { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); await page.setContent( - getHtml.call(this, 'log.html', { - plugins: '[rrwebConsoleRecord.getRecordConsolePlugin()]', + getHtml('log.html', { + plugins: ('[rrwebConsoleRecord.getRecordConsolePlugin()]' as unknown) as RecordPlugin[], }), ); diff --git a/packages/rrweb/tsconfig.json b/packages/rrweb/tsconfig.json index 57ec0c4979..c95913f748 100644 --- a/packages/rrweb/tsconfig.json +++ b/packages/rrweb/tsconfig.json @@ -11,7 +11,8 @@ "outDir": "build", "lib": ["es6", "dom"], "downlevelIteration": true, - "importsNotUsedAsValues": "error" + "importsNotUsedAsValues": "error", + "strictBindCallApply": true }, "exclude": ["test"], "include": [ diff --git a/yarn.lock b/yarn.lock index a0fa36e523..e4bb7ec301 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7931,10 +7931,10 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -mitt@^1.1.3: - version "1.2.0" - resolved "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz" - integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== +mitt@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" + integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== mkdirp-classic@^0.5.2: version "0.5.3" From be1aaa72b69d19a6fc97af32d8f3ff228ab5799f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 02/52] refactor: fix more eslint errors in the record module --- .../console/record/error-stack-parser.ts | 21 +--- .../rrweb/src/plugins/console/record/index.ts | 116 +++++++++--------- packages/rrweb/src/record/observer.ts | 65 ++++++---- packages/rrweb/src/types.ts | 14 ++- .../typings/plugins/console/record/index.d.ts | 2 +- packages/rrweb/typings/types.d.ts | 6 +- packages/rrweb/typings/utils.d.ts | 2 +- 7 files changed, 114 insertions(+), 112 deletions(-) diff --git a/packages/rrweb/src/plugins/console/record/error-stack-parser.ts b/packages/rrweb/src/plugins/console/record/error-stack-parser.ts index d5244b9832..9b46c5820b 100644 --- a/packages/rrweb/src/plugins/console/record/error-stack-parser.ts +++ b/packages/rrweb/src/plugins/console/record/error-stack-parser.ts @@ -27,19 +27,9 @@ export class StackFrame { toString() { const lineNumber = this.lineNumber || ''; const columnNumber = this.columnNumber || ''; - if (this.functionName) { - return ( - this.functionName + - ' (' + - this.fileName + - ':' + - lineNumber + - ':' + - columnNumber + - ')' - ); - } - return this.fileName + ':' + lineNumber + ':' + columnNumber; + if (this.functionName) + return `${this.functionName} (${this.fileName}:${lineNumber}:${columnNumber})`; + return `${this.fileName}:${lineNumber}:${columnNumber}`; } } @@ -55,9 +45,6 @@ const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/; export const ErrorStackParser = { /** * Given an Error object, extract the most information from it. - * - * @param {Error} error object - * @return {Array} of StackFrames */ parse: function (error: Error): StackFrame[] { // https://github.com/rrweb-io/rrweb/issues/782 @@ -65,8 +52,10 @@ export const ErrorStackParser = { return []; } if ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore typeof error.stacktrace !== 'undefined' || + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore typeof error['opera#sourceloc'] !== 'undefined' ) { diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 914e3057a8..96aab939df 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -59,27 +59,6 @@ export type LogData = { type logCallback = (p: LogData) => void; -export type LogLevel = - | 'assert' - | 'clear' - | 'count' - | 'countReset' - | 'debug' - | 'dir' - | 'dirxml' - | 'error' - | 'group' - | 'groupCollapsed' - | 'groupEnd' - | 'info' - | 'log' - | 'table' - | 'time' - | 'timeEnd' - | 'timeLog' - | 'trace' - | 'warn'; - /* fork from interface Console */ // all kinds of console functions export type Logger = { @@ -104,13 +83,24 @@ export type Logger = { warn?: typeof console.warn; }; +export type LogLevel = keyof Logger; + function initLogObserver( cb: logCallback, win: IWindow, // top window or in an iframe - logOptions: LogRecordOptions, + options: LogRecordOptions, ): listenerHandler { + const logOptions = (options + ? Object.assign({}, defaultLogOptions, options) + : defaultLogOptions) as { + level: LogLevel[]; + lengthThreshold: number; + stringifyOptions?: StringifyOptions; + logger: Logger | 'console'; + }; const loggerType = logOptions.logger; if (!loggerType) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } let logger: Logger; @@ -122,10 +112,11 @@ function initLogObserver( let logCount = 0; const cancelHandlers: listenerHandler[] = []; // add listener to thrown errors - if (logOptions.level!.includes('error')) { + if (logOptions.level.includes('error')) { if (window) { const errorHandler = (event: ErrorEvent) => { - const { message, error } = event; + const message = event.message, + error = event.error as Error; const trace: string[] = ErrorStackParser.parse( error, ).map((stackFrame: StackFrame) => stackFrame.toString()); @@ -142,7 +133,7 @@ function initLogObserver( }); } } - for (const levelType of logOptions.level!) { + for (const levelType of logOptions.level) { cancelHandlers.push(replace(logger, levelType)); } return () => { @@ -151,46 +142,51 @@ function initLogObserver( /** * replace the original console function and record logs - * @param logger the logger object such as Console - * @param level the name of log function to be replaced + * @param logger - the logger object such as Console + * @param level - the name of log function to be replaced */ function replace(_logger: Logger, level: LogLevel) { if (!_logger[level]) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } // replace the logger.{level}. return a restore function - return patch(_logger, level, (original) => { - return (...args: Array) => { - original.apply(this, args); - try { - const trace = ErrorStackParser.parse(new Error()) - .map((stackFrame: StackFrame) => stackFrame.toString()) - .splice(1); // splice(1) to omit the hijacked log function - const payload = args.map((s) => - stringify(s, logOptions.stringifyOptions), - ); - logCount++; - if (logCount < logOptions.lengthThreshold!) { - cb({ - level, - trace, - payload, - }); - } else if (logCount === logOptions.lengthThreshold) { - // notify the user - cb({ - level: 'warn', - trace: [], - payload: [ - stringify('The number of log records reached the threshold.'), - ], - }); + return patch( + _logger, + level, + (original: (...args: Array) => void) => { + return (...args: Array) => { + original.apply(this, args); + try { + const trace = ErrorStackParser.parse(new Error()) + .map((stackFrame: StackFrame) => stackFrame.toString()) + .splice(1); // splice(1) to omit the hijacked log function + const payload = args.map((s) => + stringify(s, logOptions.stringifyOptions), + ); + logCount++; + if (logCount < logOptions.lengthThreshold) { + cb({ + level, + trace, + payload, + }); + } else if (logCount === logOptions.lengthThreshold) { + // notify the user + cb({ + level: 'warn', + trace: [], + payload: [ + stringify('The number of log records reached the threshold.'), + ], + }); + } + } catch (error) { + original('rrweb logger error:', error, ...args); } - } catch (error) { - original('rrweb logger error:', error, ...args); - } - }; - }); + }; + }, + ); } } @@ -201,7 +197,5 @@ export const getRecordConsolePlugin: ( ) => RecordPlugin = (options) => ({ name: PLUGIN_NAME, observer: initLogObserver, - options: options - ? Object.assign({}, defaultLogOptions, options) - : defaultLogOptions, + options: options, }); diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 72a6043f54..77069c8536 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -129,6 +129,7 @@ function initMoveObserver({ mirror, }: observerParam): listenerHandler { if (sampling.mousemove === false) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } @@ -209,6 +210,7 @@ function initMouseInteractionObserver({ sampling, }: observerParam): listenerHandler { if (sampling.mouseInteraction === false) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } const disableMap: Record = @@ -438,7 +440,7 @@ function initInputObserver({ hookSetter(p[0], p[1], { set() { // mock to a normal event - eventHandler({ target: this } as Event); + eventHandler({ target: this as EventTarget } as Event); }, }), ), @@ -497,26 +499,26 @@ function initStyleSheetObserver( rule: string, index?: number, ) { - const id = mirror.getId(this.ownerNode); + const id = mirror.getId(this.ownerNode as Node); if (id !== -1) { styleSheetRuleCb({ id, adds: [{ rule, index }], }); } - return insertRule.apply(this, arguments); + return insertRule.apply(this, [rule, index]); }; const deleteRule = win.CSSStyleSheet.prototype.deleteRule; win.CSSStyleSheet.prototype.deleteRule = function (index: number) { - const id = mirror.getId(this.ownerNode); + const id = mirror.getId(this.ownerNode as Node); if (id !== -1) { styleSheetRuleCb({ id, removes: [{ index }], }); } - return deleteRule.apply(this, arguments); + return deleteRule.apply(this, [index]); }; const supportedNestedCSSRuleTypes: { @@ -554,7 +556,7 @@ function initStyleSheetObserver( }; type.prototype.insertRule = function (rule: string, index?: number) { - const id = mirror.getId(this.parentStyleSheet.ownerNode); + const id = mirror.getId(this.parentStyleSheet.ownerNode as Node); if (id !== -1) { styleSheetRuleCb({ id, @@ -562,25 +564,27 @@ function initStyleSheetObserver( { rule, index: [ - ...getNestedCSSRulePositions(this), + ...getNestedCSSRulePositions(this as CSSRule), index || 0, // defaults to 0 ], }, ], }); } - return unmodifiedFunctions[typeKey].insertRule.apply(this, arguments); + return unmodifiedFunctions[typeKey].insertRule.apply(this, [rule, index]); }; type.prototype.deleteRule = function (index: number) { - const id = mirror.getId(this.parentStyleSheet.ownerNode); + const id = mirror.getId(this.parentStyleSheet.ownerNode as Node); if (id !== -1) { styleSheetRuleCb({ id, - removes: [{ index: [...getNestedCSSRulePositions(this), index] }], + removes: [ + { index: [...getNestedCSSRulePositions(this as CSSRule), index] }, + ], }); } - return unmodifiedFunctions[typeKey].deleteRule.apply(this, arguments); + return unmodifiedFunctions[typeKey].deleteRule.apply(this, [index]); }; }); @@ -617,7 +621,7 @@ function initStyleDeclarationObserver( index: getNestedCSSRulePositions(this.parentRule!), }); } - return setProperty.apply(this, arguments); + return setProperty.apply(this, [property, value, priority]); }; const removeProperty = win.CSSStyleDeclaration.prototype.removeProperty; @@ -635,7 +639,7 @@ function initStyleDeclarationObserver( index: getNestedCSSRulePositions(this.parentRule!), }); } - return removeProperty.apply(this, arguments); + return removeProperty.apply(this, [property]); }; return () => { @@ -679,6 +683,7 @@ function initMediaInteractionObserver({ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { const win = doc.defaultView as IWindow; if (!win) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } @@ -689,7 +694,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { const originalFontFace = win.FontFace; win.FontFace = (function FontFace( family: string, - source: string | ArrayBufferView, + source: string | ArrayBufferLike, descriptors?: FontFaceDescriptors, ) { const fontFace = new originalFontFace(family, source, descriptors); @@ -701,23 +706,27 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { typeof source === 'string' ? source : // tslint:disable-next-line: no-any - JSON.stringify(Array.from(new Uint8Array(source as any))), + JSON.stringify(Array.from(new Uint8Array(source))), }); return fontFace; } as unknown) as typeof FontFace; - const restoreHandler = patch(doc.fonts, 'add', function (original) { - return function (this: FontFaceSet, fontFace: FontFace) { - setTimeout(() => { - const p = fontMap.get(fontFace); - if (p) { - fontCb(p); - fontMap.delete(fontFace); - } - }, 0); - return original.apply(this, [fontFace]); - }; - }); + const restoreHandler = patch( + doc.fonts, + 'add', + function (original: (font: FontFace) => void) { + return function (this: FontFaceSet, fontFace: FontFace) { + setTimeout(() => { + const p = fontMap.get(fontFace); + if (p) { + fontCb(p); + fontMap.delete(fontFace); + } + }, 0); + return original.apply(this, [fontFace]); + }; + }, + ); handlers.push(() => { win.FontFace = originalFontFace; @@ -817,6 +826,7 @@ export function initObservers( ): listenerHandler { const currentWindow = o.doc.defaultView; // basically document.window if (!currentWindow) { + // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; } @@ -833,6 +843,7 @@ export function initObservers( const styleDeclarationObserver = initStyleDeclarationObserver(o, { win: currentWindow, }); + // eslint-disable-next-line @typescript-eslint/no-empty-function const fontObserver = o.collectFonts ? initFontObserver(o) : () => {}; // plugins const pluginHandlers: listenerHandler[] = []; diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 7a8e6a8d99..b26885bed5 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -218,7 +218,11 @@ export type SamplingStrategy = Partial<{ export type RecordPlugin = { name: string; - observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler; + observer?: ( + cb: (...args: Array) => void, + win: IWindow, + options: TOptions, + ) => listenerHandler; eventProcessor?: (event: eventWithTime) => eventWithTime & TExtend; options: TOptions; }; @@ -285,8 +289,12 @@ export type observerParam = { shadowDomManager: ShadowDomManager; canvasManager: CanvasManager; plugins: Array<{ - observer: Function; - callback: Function; + observer: ( + cb: (...arg: Array) => void, + win: IWindow, + options: unknown, + ) => listenerHandler; + callback: (...arg: Array) => void; options: unknown; }>; }; diff --git a/packages/rrweb/typings/plugins/console/record/index.d.ts b/packages/rrweb/typings/plugins/console/record/index.d.ts index dc8744ab08..9b3b8de44d 100644 --- a/packages/rrweb/typings/plugins/console/record/index.d.ts +++ b/packages/rrweb/typings/plugins/console/record/index.d.ts @@ -15,7 +15,6 @@ export declare type LogData = { trace: string[]; payload: string[]; }; -export declare type LogLevel = 'assert' | 'clear' | 'count' | 'countReset' | 'debug' | 'dir' | 'dirxml' | 'error' | 'group' | 'groupCollapsed' | 'groupEnd' | 'info' | 'log' | 'table' | 'time' | 'timeEnd' | 'timeLog' | 'trace' | 'warn'; export declare type Logger = { assert?: typeof console.assert; clear?: typeof console.clear; @@ -37,6 +36,7 @@ export declare type Logger = { trace?: typeof console.trace; warn?: typeof console.warn; }; +export declare type LogLevel = keyof Logger; export declare const PLUGIN_NAME = "rrweb/console@1"; export declare const getRecordConsolePlugin: (options?: LogRecordOptions) => RecordPlugin; export {}; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index c40a9f2a29..c1f7047794 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -134,7 +134,7 @@ export declare type SamplingStrategy = Partial<{ }>; export declare type RecordPlugin = { name: string; - observer?: (cb: Function, win: IWindow, options: TOptions) => listenerHandler; + observer?: (cb: (...args: Array) => void, win: IWindow, options: TOptions) => listenerHandler; eventProcessor?: (event: eventWithTime) => eventWithTime & TExtend; options: TOptions; }; @@ -198,8 +198,8 @@ export declare type observerParam = { shadowDomManager: ShadowDomManager; canvasManager: CanvasManager; plugins: Array<{ - observer: Function; - callback: Function; + observer: (cb: (...arg: Array) => void, win: IWindow, options: unknown) => listenerHandler; + callback: (...arg: Array) => void; options: unknown; }>; }; diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index ed87d5fee1..24000e0c58 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -3,7 +3,7 @@ import type { IMirror, Mirror } from 'rrweb-snapshot'; import type { RRNode, RRIFrameElement } from 'rrdom'; export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler; export declare let _mirror: DeprecatedMirror; -export declare function throttle(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void; +export declare function throttle(func: (arg: T) => void, wait: number, options?: throttleOptions): (...args: T[]) => void; export declare function hookSetter(target: T, key: string | number | symbol, d: PropertyDescriptor, isRevoked?: boolean, win?: Window & typeof globalThis): hookResetter; export declare function patch(source: { [key: string]: any; From 2aa4e86efbcf7f9d9fca069d35454735357f71d7 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 03/52] feat: define an interface of session cutter --- packages/rrdom/rollup.config.js | 5 +++ packages/rrdom/src/trim.ts | 33 +++++++++++++++ packages/rrdom/test/session-cutter.test.ts | 49 ++++++++++++++++++++++ packages/rrdom/test/virtual-dom.test.ts | 7 ++-- packages/rrdom/tsconfig.json | 3 +- 5 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 packages/rrdom/src/trim.ts create mode 100644 packages/rrdom/test/session-cutter.test.ts diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index 5bd346673f..cd93ef37f7 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -27,6 +27,11 @@ const baseConfigs = [ name: pkg.name, path: pkg.name, }, + { + input: './src/trim.ts', + name: pkg.name, + path: 'trim', + }, ]; let configs = []; diff --git a/packages/rrdom/src/trim.ts b/packages/rrdom/src/trim.ts new file mode 100644 index 0000000000..e852c228f1 --- /dev/null +++ b/packages/rrdom/src/trim.ts @@ -0,0 +1,33 @@ +import type { eventWithTime } from 'rrweb/src/types'; +type CutterConfig = { + points: number[]; +}; +export function sessionCut(events: eventWithTime[], config: CutterConfig) { + // Events length is too short so that cutting process is not needed. + if (events.length < 2) return events; + const { points } = config; + if (!points || points.length == 0) return events; + const totalTime = events[events.length - 1].timestamp - events[0].timestamp; + + const validSortedPoints = getValidSortedPoints(points, totalTime); + + return; +} + +function vitualPlay( + events: eventWithTime[], + pausePoints: number[], + callback: () => void, +) { + +} + +function getValidSortedPoints(points: number[], totalTime: number) { + const validSortedPoints = []; + for (let i = 0; i < points.length; i++) { + const point = points[i]; + if (point <= 0 || point > totalTime) continue; + validSortedPoints.push(point); + } + return validSortedPoints.sort(); +} diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrdom/test/session-cutter.test.ts new file mode 100644 index 0000000000..63cfe8944e --- /dev/null +++ b/packages/rrdom/test/session-cutter.test.ts @@ -0,0 +1,49 @@ +import rewire from 'rewire'; +import { EventType, eventWithTime } from 'rrweb/src/types'; +import { sessionCut } from '../src/trim'; +const rewiredSessionCutter = rewire('../lib/trim'); +const getValidSortedPoints = rewiredSessionCutter.__get__( + 'getValidSortedPoints', +); + +describe('session cutter', () => { + it('should return the same events if the events length is too short', () => { + const events1: eventWithTime[] = []; + const config = { points: [10] }; + expect(sessionCut(events1, config)).toEqual(events1); + + const events2: eventWithTime[] = [ + { + type: EventType.Load, + data: {}, + timestamp: 1, + } as eventWithTime, + ]; + expect(sessionCut(events2, config)).toEqual(events2); + }); + + it('should return the same events if the points length is 0', () => { + const events: eventWithTime[] = [ + { + type: EventType.Load, + data: {}, + timestamp: 1, + } as eventWithTime, + { + type: EventType.Meta, + data: {}, + timestamp: 2, + } as eventWithTime, + ]; + const config = { points: [] }; + expect(sessionCut(events, config)).toEqual(events); + }); + + it('should sort and validate cutting points array', () => { + const inputPoints = [10, 250.5, -10, -1, 0, 100]; + expect(getValidSortedPoints([], 100)).toEqual([]); + expect(getValidSortedPoints(inputPoints, 10)).toEqual([10]); + expect(getValidSortedPoints(inputPoints, 100)).toEqual([10, 100]); + expect(getValidSortedPoints(inputPoints, 300)).toEqual([10, 100, 250.5]); + }); +}); diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index b99a34325e..ef9e940b49 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -3,10 +3,10 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import puppeteer from 'puppeteer'; import * as rollup from 'rollup'; import resolve from '@rollup/plugin-node-resolve'; -import * as typescript from 'rollup-plugin-typescript2'; +import typescript from 'rollup-plugin-typescript2'; import { JSDOM } from 'jsdom'; import { cdataNode, @@ -30,7 +30,6 @@ import { BaseRRNode as RRNode, } from '../src'; -const _typescript = (typescript as unknown) as typeof typescript.default; const printRRDomCode = ` /** * Print the RRDom as a string. @@ -222,7 +221,7 @@ describe('RRDocument for browser environment', () => { input: path.resolve(__dirname, '../src/index.ts'), plugins: [ (resolve() as unknown) as rollup.Plugin, - (_typescript({ + (typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, }) as unknown) as rollup.Plugin, ], diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index c5d366adf8..7f2ee95431 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -12,7 +12,8 @@ "lib": ["es6", "dom"], "skipLibCheck": true, "declaration": true, - "importsNotUsedAsValues": "error" + "importsNotUsedAsValues": "error", + "esModuleInterop": true }, "compileOnSave": true, "exclude": ["test"], From dca528df97c772b50f9e50b03464e7d14abd9f28 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 04/52] feat: implement a SyncReplayer and add session cut interface --- packages/rrdom/package.json | 4 +- packages/rrdom/rollup.config.js | 4 +- packages/rrdom/src/tools/SyncReplayer.ts | 892 ++++++++++++++++++ packages/rrdom/src/tools/session-cutter.ts | 72 ++ packages/rrdom/src/trim.ts | 33 - .../__snapshots__/session-cutter.test.ts.snap | 78 ++ packages/rrdom/test/events/mutation.event.ts | 284 ++++++ packages/rrdom/test/session-cutter.test.ts | 40 +- packages/rrdom/tsconfig.json | 4 +- yarn.lock | 209 +++- 10 files changed, 1573 insertions(+), 47 deletions(-) create mode 100644 packages/rrdom/src/tools/SyncReplayer.ts create mode 100644 packages/rrdom/src/tools/session-cutter.ts delete mode 100644 packages/rrdom/src/trim.ts create mode 100644 packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap create mode 100644 packages/rrdom/test/events/mutation.event.ts diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index c80ee641b5..341f116cc3 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -32,12 +32,14 @@ "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", "@types/jest": "^27.4.1", + "@types/puppeteer": "^5.4.4", + "@types/rewire": "^2.5.28", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", - "@types/puppeteer": "^5.4.4", "eslint": "^8.15.0", "jest": "^27.5.1", "puppeteer": "^9.1.1", + "rewire": "^6.0.0", "rollup": "^2.56.3", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index cd93ef37f7..ddb0e69cca 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -28,9 +28,9 @@ const baseConfigs = [ path: pkg.name, }, { - input: './src/trim.ts', + input: './src/tools/session-cutter.ts', name: pkg.name, - path: 'trim', + path: 'session-cutter', }, ]; diff --git a/packages/rrdom/src/tools/SyncReplayer.ts b/packages/rrdom/src/tools/SyncReplayer.ts new file mode 100644 index 0000000000..df3c6926a6 --- /dev/null +++ b/packages/rrdom/src/tools/SyncReplayer.ts @@ -0,0 +1,892 @@ +import { + rebuild, + buildNodeWithSN, + NodeType, + BuildCache, + createCache, + IMirror, + Mirror, +} from 'rrweb-snapshot'; +import { RRDocument as BaseRRDocument, StyleRuleType } from '../index'; +import type { + RRNode, + RRElement, + RRStyleElement, + RRIFrameElement, + RRMediaElement, + RRCanvasElement, + Mirror as RRDOMMirror, + VirtualStyleRules, +} from '../index'; +import * as mittProxy from 'mitt'; +import { + EventType, + IncrementalSource, + fullSnapshotEvent, + eventWithTime, + playerConfig, + playerMetaData, + addedNodeMutation, + incrementalSnapshotEvent, + incrementalData, + ReplayerEvents, + Handler, + Emitter, + MediaInteractions, + metaEvent, + mutationData, + styleValueWithPriority, + mouseMovePos, + canvasEventWithTime, +} from 'rrweb/src/types'; +import { + queueToResolveTrees, + iterateResolveTree, + hasShadowRoot, + isSerializedIframe, + uniqueTextMutations, +} from 'rrweb/src/utils'; +import type { Replayer } from 'rrweb/src/replay'; + +// https://github.com/rollup/rollup/issues/1267#issuecomment-296395734 +const mitt = mittProxy.default || mittProxy; + +const REPLAY_CONSOLE_PREFIX = '[replayer]'; + +class RRDocument extends BaseRRDocument { + scrollTop: number; + scrollLeft: number; +} + +type missingNodeMap = { + [id: number]: { node: RRNode; mutation: addedNodeMutation }; +}; + +type AppendedIframe = { + mutationInQueue: addedNodeMutation; + builtNode: RRIFrameElement; +}; + +export class SyncReplayer { + public config: Partial; + + public virtualDom: RRDocument = new RRDocument(); + + public mousePos: mouseMovePos | null = null; + + public events: eventWithTime[]; + + public unhandledEvents: eventWithTime[] = []; + + private currentTime = 0; + + private emitter: Emitter = mitt(); + + // tslint:disable-next-line: variable-name + private legacy_missingNodeRetryMap: missingNodeMap = {}; + + // The replayer uses the cache to speed up replay and scrubbing. + private cache: BuildCache = createCache(); + + private mirror: RRDOMMirror = this.virtualDom.mirror; + + private firstFullSnapshot: eventWithTime | true | null = null; + + private newDocumentQueue: addedNodeMutation[] = []; + + constructor( + events: Array, + config?: Partial, + ) { + if (events.length < 2) { + throw new Error('Replayer need at least 2 events.'); + } + this.events = events + .map((e) => { + if (config && config.unpackFn) { + return config.unpackFn(e as string); + } + return e as eventWithTime; + }) + .sort((a1, a2) => a1.timestamp - a2.timestamp); + this.getCastFn = this.getCastFn.bind(this); + + const defaultConfig = { + showWarning: true, + showDebug: false, + liveMode: false, + }; + this.config = Object.assign({}, defaultConfig, config); + + this.emitter.on(ReplayerEvents.PlayBack, () => { + this.firstFullSnapshot = null; + this.mirror.reset(); + }); + + // rebuild first full snapshot as the poster of the player + // maybe we can cache it for performance optimization + const firstMeta = this.events.find((e) => e.type === EventType.Meta); + const firstFullsnapshot = this.events.find( + (e) => e.type === EventType.FullSnapshot, + ); + if (firstMeta) { + const { width, height } = firstMeta.data as metaEvent['data']; + setTimeout(() => { + this.emitter.emit(ReplayerEvents.Resize, { + width, + height, + }); + }, 0); + } + if (firstFullsnapshot) { + setTimeout(() => { + // when something has been played, there is no need to rebuild poster + if (this.firstFullSnapshot) { + // true if any other fullSnapshot has been executed by Timer already + return; + } + this.firstFullSnapshot = firstFullsnapshot; + this.rebuildFullSnapshot( + firstFullsnapshot as fullSnapshotEvent & { timestamp: number }, + ); + const initialOffset = (firstFullsnapshot as fullSnapshotEvent).data + .initialOffset; + this.virtualDom.scrollTop = initialOffset.top; + this.virtualDom.scrollLeft = initialOffset.left; + }, 1); + } + } + + public on(event: string, handler: Handler) { + this.emitter.on(event, handler); + return this; + } + + public off(event: string, handler: Handler) { + this.emitter.off(event, handler); + return this; + } + + public setConfig(config: Partial) { + Object.keys(config).forEach((key) => { + const newConfigValue = config[key as keyof playerConfig]; + (this.config as Record)[ + key as keyof playerConfig + ] = config[key as keyof playerConfig]; + }); + } + + public getMetaData(): playerMetaData { + const firstEvent = this.events[0]; + const lastEvent = this.events[this.events.length - 1]; + return { + startTime: firstEvent.timestamp, + endTime: lastEvent.timestamp, + totalTime: lastEvent.timestamp - firstEvent.timestamp, + }; + } + + public getCurrentTime(): number { + return this.currentTime; + } + + public getMirror() { + return this.mirror; + } + + public play( + castEventCallback?: (event: { + index: number; + event: eventWithTime; + currentTime: number; + }) => boolean | void, + ) { + const baseTime = this.events[0].timestamp; + for (let i = 0; i < this.events.length; i++) { + const event = this.events[i]; + const castFn = this.getCastFn(event); + castFn(); + this.currentTime = event.timestamp - baseTime; + if ( + castEventCallback?.({ + index: i, + event: event, + currentTime: this.currentTime, + }) === false + ) + break; + } + } + + /** + * Empties the replayer's cache and reclaims memory. + * The replayer will use this cache to speed up the playback. + */ + public resetCache() { + this.cache = createCache(); + } + + private getCastFn(event: eventWithTime) { + let castFn: undefined | (() => void); + switch (event.type) { + case EventType.DomContentLoaded: + case EventType.Load: + break; + case EventType.Custom: + castFn = () => { + /** + * emit custom-event and pass the event object. + * + * This will add more value to the custom event and allows the client to react for custom-event. + */ + this.emitter.emit(ReplayerEvents.CustomEvent, event); + }; + break; + case EventType.Meta: + castFn = () => + this.emitter.emit(ReplayerEvents.Resize, { + width: event.data.width, + height: event.data.height, + }); + break; + case EventType.FullSnapshot: + castFn = () => { + if (this.firstFullSnapshot) { + if (this.firstFullSnapshot === event) { + // we've already built this exact FullSnapshot when the player was mounted, and haven't built any other FullSnapshot since + this.firstFullSnapshot = true; // forget as we might need to re-execute this FullSnapshot later e.g. to rebuild after scrubbing + return; + } + } else { + // Timer (requestAnimationFrame) can be faster than setTimeout(..., 1) + this.firstFullSnapshot = true; + } + this.rebuildFullSnapshot(event); + this.virtualDom.scrollTop = event.data.initialOffset.top; + this.virtualDom.scrollLeft = event.data.initialOffset.left; + }; + break; + case EventType.IncrementalSnapshot: + castFn = () => { + this.applyIncremental(event); + }; + break; + default: + } + const wrappedCastFn = () => { + if (castFn) { + castFn(); + } + + for (const plugin of this.config.plugins || []) { + plugin.handler(event, true, { + replayer: (this as unknown) as Replayer, + }); + } + + this.emitter.emit(ReplayerEvents.EventCast, event); + }; + return wrappedCastFn; + } + + private rebuildFullSnapshot( + event: fullSnapshotEvent & { timestamp: number }, + ) { + if (Object.keys(this.legacy_missingNodeRetryMap).length) { + console.warn( + 'Found unresolved missing node map', + this.legacy_missingNodeRetryMap, + ); + } + this.legacy_missingNodeRetryMap = {}; + const collected: AppendedIframe[] = []; + rebuild(event.data.node, { + doc: (this.virtualDom as unknown) as Document, + afterAppend: (builtNode) => { + this.collectIframeAndAttachDocument( + collected, + (builtNode as unknown) as RRNode, + ); + }, + cache: this.cache, + mirror: (this.mirror as unknown) as Mirror, + }); + for (const { mutationInQueue, builtNode } of collected) { + this.attachDocumentToIframe( + mutationInQueue, + (builtNode as unknown) as RRIFrameElement, + ); + this.newDocumentQueue = this.newDocumentQueue.filter( + (m) => m !== mutationInQueue, + ); + } + this.emitter.emit(ReplayerEvents.FullsnapshotRebuilded, event); + } + + private attachDocumentToIframe( + mutation: addedNodeMutation, + iframeEl: RRIFrameElement, + ) { + const collected: AppendedIframe[] = []; + buildNodeWithSN(mutation.node, { + doc: (iframeEl.contentDocument as unknown) as Document, + mirror: (this.mirror as unknown) as Mirror, + hackCss: true, + skipChild: false, + cache: this.cache, + }); + for (const { mutationInQueue, builtNode } of collected) { + this.attachDocumentToIframe(mutationInQueue, builtNode); + this.newDocumentQueue = this.newDocumentQueue.filter( + (m) => m !== mutationInQueue, + ); + } + } + + private collectIframeAndAttachDocument( + collected: AppendedIframe[], + builtNode: RRNode, + ) { + if ( + isSerializedIframe( + (builtNode as unknown) as Node, + (this.mirror as unknown) as IMirror, + ) + ) { + const mutationInQueue = this.newDocumentQueue.find( + (m) => m.parentId === this.mirror.getId(builtNode), + ); + if (mutationInQueue) { + collected.push({ + mutationInQueue, + builtNode: builtNode as RRIFrameElement, + }); + } + } + } + + private applyIncremental( + e: incrementalSnapshotEvent & { timestamp: number; delay?: number }, + ) { + const { data: d } = e; + switch (d.source) { + case IncrementalSource.Mutation: { + try { + this.applyMutation(d); + } catch (error) { + this.warn(`Exception in mutation ${error.message || error}`, d); + } + break; + } + case IncrementalSource.Drag: + case IncrementalSource.TouchMove: + case IncrementalSource.MouseMove: + { + const lastPosition = d.positions[d.positions.length - 1]; + this.mousePos = { + x: lastPosition.x, + y: lastPosition.y, + id: lastPosition.id, + debugData: d, + }; + } + break; + case IncrementalSource.MouseInteraction: + break; + case IncrementalSource.Scroll: { + /** + * Same as the situation of missing input target. + */ + if (d.id === -1) { + break; + } + + const target = this.virtualDom.mirror.getNode(d.id) as RRElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + target.scrollData = d; + break; + } + case IncrementalSource.ViewportResize: + this.emitter.emit(ReplayerEvents.Resize, { + width: d.width, + height: d.height, + }); + break; + case IncrementalSource.Input: { + /** + * Input event on an unserialized node usually means the event + * was synchrony triggered programmatically after the node was + * created. This means there was not an user observable interaction + * and we do not need to replay it. + */ + if (d.id === -1) { + break; + } + const target = this.virtualDom.mirror.getNode(d.id) as RRElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + target.inputData = d; + break; + } + case IncrementalSource.MediaInteraction: { + const target = this.mirror.getNode(d.id); + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + const mediaEl = target as RRMediaElement; + try { + if (d.currentTime) { + mediaEl.currentTime = d.currentTime; + } + if (d.volume) { + mediaEl.volume = d.volume; + } + if (d.muted) { + mediaEl.muted = d.muted; + } + if (d.type === MediaInteractions.Pause) { + mediaEl.pause(); + } + if (d.type === MediaInteractions.Play) { + // remove listener for 'canplay' event because play() is async and returns a promise + // i.e. media will evntualy start to play when data is loaded + // 'canplay' event fires even when currentTime attribute changes which may lead to + // unexpeted behavior + void mediaEl.play(); + } + } catch (error) { + if (this.config.showWarning) { + console.warn( + `Failed to replay media interactions: ${error.message || error}`, + ); + } + } + break; + } + case IncrementalSource.StyleSheetRule: { + const target = this.virtualDom.mirror.getNode(d.id) as RRStyleElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + const rules: VirtualStyleRules = target.rules; + d.adds?.forEach(({ rule, index: nestedIndex }) => + rules?.push({ + cssText: rule, + index: nestedIndex, + type: StyleRuleType.Insert, + }), + ); + d.removes?.forEach(({ index: nestedIndex }) => + rules?.push({ index: nestedIndex, type: StyleRuleType.Remove }), + ); + break; + } + case IncrementalSource.StyleDeclaration: { + const target = this.mirror.getNode(d.id) as RRStyleElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + const rules: VirtualStyleRules = target.rules; + d.set && + rules.push({ + type: StyleRuleType.SetProperty, + index: d.index, + ...d.set, + }); + d.remove && + rules.push({ + type: StyleRuleType.RemoveProperty, + index: d.index, + ...d.remove, + }); + + break; + } + case IncrementalSource.CanvasMutation: { + if (!this.config.UNSAFE_replayCanvas) { + return; + } + + const target = this.virtualDom.mirror.getNode(d.id) as RRCanvasElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + target.canvasMutations.push({ + event: e as canvasEventWithTime, + mutation: d, + }); + break; + } + case IncrementalSource.Font: { + this.unhandledEvents.push(e); + break; + } + default: + } + } + + private applyMutation(d: mutationData) { + const mirror = this.mirror; + + d.removes.forEach((mutation) => { + const target = mirror.getNode(mutation.id); + if (!target) { + if (d.removes.find((r) => r.id === mutation.parentId)) { + // no need to warn, parent was already removed + return; + } + return this.warnNodeNotFound(d, mutation.id); + } + let parent = mirror.getNode(mutation.parentId); + if (!parent) { + return this.warnNodeNotFound(d, mutation.parentId); + } + if (mutation.isShadow && hasShadowRoot((parent as unknown) as Node)) { + parent = (parent as RRElement).shadowRoot; + } + // target may be removed with its parents before + mirror.removeNodeFromMap(target); + if (parent) + try { + parent.removeChild(target); + /** + * https://github.com/rrweb-io/rrweb/pull/887 + * Remove any virtual style rules for stylesheets if a child text node is removed. + */ + if ( + target.nodeName === '#text' && + parent.nodeName === 'STYLE' && + (parent as RRStyleElement).rules?.length > 0 + ) + (parent as RRStyleElement).rules = []; + } catch (error) { + if (error instanceof DOMException) { + this.warn( + 'parent could not remove child in mutation', + parent, + target, + d, + ); + } else { + throw error; + } + } + }); + + // tslint:disable-next-line: variable-name + const legacy_missingNodeMap = { + ...this.legacy_missingNodeRetryMap, + }; + const queue: addedNodeMutation[] = []; + + // next not present at this moment + const nextNotInDOM = (mutation: addedNodeMutation) => { + let next: RRNode | null = null; + if (mutation.nextId) { + next = mirror.getNode(mutation.nextId); + } + // next not present at this moment + if ( + mutation.nextId !== null && + mutation.nextId !== undefined && + mutation.nextId !== -1 && + !next + ) { + return true; + } + return false; + }; + + const appendNode = (mutation: addedNodeMutation) => { + let parent = mirror.getNode(mutation.parentId); + if (!parent) { + if (mutation.node.type === NodeType.Document) { + // is newly added document, maybe the document node of an iframe + return this.newDocumentQueue.push(mutation); + } + return queue.push(mutation); + } + if (mutation.node.isShadow) { + // If the parent is attached a shadow dom after it's created, it won't have a shadow root. + if (!hasShadowRoot((parent as unknown) as Node)) { + (parent as RRElement).attachShadow({ mode: 'open' }); + parent = (parent as RRElement).shadowRoot!; + } else parent = (parent as RRElement).shadowRoot!; + } + + let previous: RRNode | null = null; + let next: RRNode | null = null; + if (mutation.previousId) { + previous = mirror.getNode(mutation.previousId); + } + if (mutation.nextId) { + next = mirror.getNode(mutation.nextId); + } + if (nextNotInDOM(mutation)) { + return queue.push(mutation); + } + + if (mutation.node.rootId && !mirror.getNode(mutation.node.rootId)) { + return; + } + + const targetDoc = mutation.node.rootId + ? mirror.getNode(mutation.node.rootId) + : this.virtualDom; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (isSerializedIframe(parent, mirror)) { + this.attachDocumentToIframe(mutation, parent as RRIFrameElement); + return; + } + const target = (buildNodeWithSN(mutation.node, { + doc: (targetDoc as unknown) as Document, // can be Document or RRDocument + mirror: (mirror as unknown) as Mirror, + skipChild: true, + hackCss: true, + cache: this.cache, + }) as unknown) as RRNode; + + // legacy data, we should not have -1 siblings any more + if (mutation.previousId === -1 || mutation.nextId === -1) { + legacy_missingNodeMap[mutation.node.id] = { + node: target, + mutation, + }; + return; + } + + const parentSn = mirror.getMeta(parent); + if ( + parentSn && + parentSn.type === NodeType.Element && + parentSn.tagName === 'textarea' && + mutation.node.type === NodeType.Text + ) { + // https://github.com/rrweb-io/rrweb/issues/745 + // parent is textarea, will only keep one child node as the value + for (const c of Array.from(parent.childNodes)) { + if (c.nodeType === parent.TEXT_NODE) { + parent.removeChild(c); + } + } + } + + if (previous && previous.nextSibling && previous.nextSibling.parentNode) { + parent.insertBefore(target, previous.nextSibling); + } else if (next && next.parentNode) { + // making sure the parent contains the reference nodes + // before we insert target before next. + parent.contains(next) + ? parent.insertBefore(target, next) + : parent.insertBefore(target, null); + } else { + /** + * Sometimes the document changes and the MutationObserver is disconnected, so the removal of child elements can't be detected and recorded. After the change of document, we may get another mutation which adds a new html element, while the old html element still exists in the dom, and we need to remove the old html element first to avoid collision. + */ + if (parent === targetDoc) { + while (targetDoc.firstChild) { + targetDoc.removeChild(targetDoc.firstChild); + } + } + + parent.appendChild(target); + } + /** + * https://github.com/rrweb-io/rrweb/pull/887 + * Remove any virtual style rules for stylesheets if a new text node is appended. + */ + if ( + target.nodeName === '#text' && + parent.nodeName === 'STYLE' && + (parent as RRStyleElement).rules?.length > 0 + ) + (parent as RRStyleElement).rules = []; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (isSerializedIframe(target, this.mirror)) { + const targetId = this.mirror.getId(target); + const mutationInQueue = this.newDocumentQueue.find( + (m) => m.parentId === targetId, + ); + if (mutationInQueue) { + this.attachDocumentToIframe( + mutationInQueue, + target as RRIFrameElement, + ); + this.newDocumentQueue = this.newDocumentQueue.filter( + (m) => m !== mutationInQueue, + ); + } + } + + if (mutation.previousId || mutation.nextId) { + this.legacy_resolveMissingNode( + legacy_missingNodeMap, + parent, + target, + mutation, + ); + } + }; + + d.adds.forEach((mutation) => { + appendNode(mutation); + }); + + const startTime = Date.now(); + while (queue.length) { + // transform queue to resolve tree + const resolveTrees = queueToResolveTrees(queue); + queue.length = 0; + if (Date.now() - startTime > 500) { + this.warn( + 'Timeout in the loop, please check the resolve tree data:', + resolveTrees, + ); + break; + } + for (const tree of resolveTrees) { + const parent = mirror.getNode(tree.value.parentId); + if (!parent) { + this.debug( + 'Drop resolve tree since there is no parent for the root node.', + tree, + ); + } else { + iterateResolveTree(tree, (mutation) => { + appendNode(mutation); + }); + } + } + } + + if (Object.keys(legacy_missingNodeMap).length) { + Object.assign(this.legacy_missingNodeRetryMap, legacy_missingNodeMap); + } + + uniqueTextMutations(d.texts).forEach((mutation) => { + const target = mirror.getNode(mutation.id); + if (!target) { + if (d.removes.find((r) => r.id === mutation.id)) { + // no need to warn, element was already removed + return; + } + return this.warnNodeNotFound(d, mutation.id); + } + target.textContent = mutation.value; + + /** + * https://github.com/rrweb-io/rrweb/pull/865 + * Remove any virtual style rules for stylesheets whose contents are replaced. + */ + const parent = target.parentNode as RRStyleElement; + if (parent?.rules?.length > 0) parent.rules = []; + }); + d.attributes.forEach((mutation) => { + const target = mirror.getNode(mutation.id); + if (!target) { + if (d.removes.find((r) => r.id === mutation.id)) { + // no need to warn, element was already removed + return; + } + return this.warnNodeNotFound(d, mutation.id); + } + for (const attributeName in mutation.attributes) { + if (typeof attributeName === 'string') { + const value = mutation.attributes[attributeName]; + if (value === null) { + (target as RRElement).removeAttribute(attributeName); + } else if (typeof value === 'string') { + try { + (target as RRElement).setAttribute(attributeName, value); + } catch (error) { + if (this.config.showWarning) { + console.warn( + 'An error occurred may due to the checkout feature.', + error, + ); + } + } + } else if (attributeName === 'style') { + const styleValues = value; + const targetEl = target as RRElement; + for (const s in styleValues) { + if (styleValues[s] === false) { + targetEl.style.removeProperty(s); + } else if (styleValues[s] instanceof Array) { + const svp = styleValues[s] as styleValueWithPriority; + targetEl.style.setProperty(s, svp[0], svp[1]); + } else { + const svs = styleValues[s] as string; + targetEl.style.setProperty(s, svs); + } + } + } + } + } + }); + } + + private legacy_resolveMissingNode( + map: missingNodeMap, + parent: RRNode, + target: RRNode, + targetMutation: addedNodeMutation, + ) { + const { previousId, nextId } = targetMutation; + const previousInMap = previousId && map[previousId]; + const nextInMap = nextId && map[nextId]; + if (previousInMap) { + const { node, mutation } = previousInMap; + parent.insertBefore(node, target); + delete map[mutation.node.id]; + delete this.legacy_missingNodeRetryMap[mutation.node.id]; + if (mutation.previousId || mutation.nextId) { + this.legacy_resolveMissingNode(map, parent, node, mutation); + } + } + if (nextInMap) { + const { node, mutation } = nextInMap; + parent.insertBefore(node, target.nextSibling); + delete map[mutation.node.id]; + delete this.legacy_missingNodeRetryMap[mutation.node.id]; + if (mutation.previousId || mutation.nextId) { + this.legacy_resolveMissingNode(map, parent, node, mutation); + } + } + } + + private warnNodeNotFound(d: incrementalData, id: number) { + this.warn(`Node with id '${id}' not found. `, d); + } + + private debugNodeNotFound(d: incrementalData, id: number) { + /** + * There maybe some valid scenes of node not being found. + * Because DOM events are macrotask and MutationObserver callback + * is microtask, so events fired on a removed DOM may emit + * snapshots in the reverse order. + */ + this.debug(REPLAY_CONSOLE_PREFIX, `Node with id '${id}' not found. `, d); + } + + private warn(...args: Parameters) { + if (!this.config.showWarning) { + return; + } + console.warn(REPLAY_CONSOLE_PREFIX, ...args); + } + + private debug(...args: Parameters) { + if (!this.config.showDebug) { + return; + } + // tslint:disable-next-line: no-console + console.log(REPLAY_CONSOLE_PREFIX, ...args); + } +} diff --git a/packages/rrdom/src/tools/session-cutter.ts b/packages/rrdom/src/tools/session-cutter.ts new file mode 100644 index 0000000000..e7b0851e50 --- /dev/null +++ b/packages/rrdom/src/tools/session-cutter.ts @@ -0,0 +1,72 @@ +import type { eventWithTime } from 'rrweb/src/types'; +import { SyncReplayer } from './SyncReplayer'; +type CutterConfig = { + points: number[]; +}; +export function sessionCut(events: eventWithTime[], config: CutterConfig) { + // Events length is too short so that cutting process is not needed. + if (events.length < 2) return events; + const { points } = config; + if (!points || points.length == 0) return events; + + events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); + const totalTime = events[events.length - 1].timestamp - events[0].timestamp; + + const validSortedPoints = getValidSortedPoints(points, totalTime); + if (validSortedPoints.length < 1) return [events]; + const results: eventWithTime[][] = []; + const replayer = new SyncReplayer(events); + let cutPointIndex = 0; + const baseTime = events[0].timestamp; + const validSortedTimestamp = validSortedPoints.map( + (point) => baseTime + point, + ); + replayer.play(({ index, event }) => { + if ( + event.timestamp < validSortedTimestamp[cutPointIndex] && + index + 1 < events.length + ) { + const nextEvent = events[index + 1]; + while ( + cutPointIndex < validSortedTimestamp.length && + nextEvent.timestamp > validSortedTimestamp[cutPointIndex] + ) { + if (results.length === 0) { + results.push(events.slice(0, index + 1)); + } + cutPointIndex++; + const nextCutTimestamp = + cutPointIndex < validSortedPoints.length + ? validSortedTimestamp[cutPointIndex] + : events[events.length - 1].timestamp; + const result = cutEvents( + events.slice(index + 1), + replayer, + nextCutTimestamp, + ); + results.push(result); + } + return cutPointIndex < validSortedTimestamp.length; + } + }); + return results; +} + +function cutEvents( + events: eventWithTime[], + replayer: SyncReplayer, + endTimestamp: number, +) { + // TODO + return []; +} + +function getValidSortedPoints(points: number[], totalTime: number) { + const validSortedPoints = []; + for (let i = 0; i < points.length; i++) { + const point = points[i]; + if (point <= 0 || point > totalTime) continue; + validSortedPoints.push(point); + } + return validSortedPoints.sort(); +} diff --git a/packages/rrdom/src/trim.ts b/packages/rrdom/src/trim.ts deleted file mode 100644 index e852c228f1..0000000000 --- a/packages/rrdom/src/trim.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { eventWithTime } from 'rrweb/src/types'; -type CutterConfig = { - points: number[]; -}; -export function sessionCut(events: eventWithTime[], config: CutterConfig) { - // Events length is too short so that cutting process is not needed. - if (events.length < 2) return events; - const { points } = config; - if (!points || points.length == 0) return events; - const totalTime = events[events.length - 1].timestamp - events[0].timestamp; - - const validSortedPoints = getValidSortedPoints(points, totalTime); - - return; -} - -function vitualPlay( - events: eventWithTime[], - pausePoints: number[], - callback: () => void, -) { - -} - -function getValidSortedPoints(points: number[], totalTime: number) { - const validSortedPoints = []; - for (let i = 0; i < points.length; i++) { - const point = points[i]; - if (point <= 0 || point > totalTime) continue; - validSortedPoints.push(point); - } - return validSortedPoints.sort(); -} diff --git a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap new file mode 100644 index 0000000000..1cff10690e --- /dev/null +++ b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap @@ -0,0 +1,78 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Full Snapshot @ 100 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 18 RRText text=\\"\\\\nempty\\\\n\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 1000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 34 DIV + 35 RRText text=\\"1\\" + 33 DIV + 36 RRText text=\\"2\\" + 32 DIV + 37 RRText text=\\"3\\" + 31 DIV + 38 RRText text=\\"4\\" + 29 DIV + 30 RRText text=\\"5\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 2000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 29 DIV + 30 RRText text=\\"5\\" + 31 DIV + 38 RRText text=\\"4\\" + 32 DIV + 37 RRText text=\\"3\\" + 33 DIV + 36 RRText text=\\"2\\" + 34 DIV + 35 RRText text=\\"1\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 3000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; diff --git a/packages/rrdom/test/events/mutation.event.ts b/packages/rrdom/test/events/mutation.event.ts new file mode 100644 index 0000000000..fcd4934f2f --- /dev/null +++ b/packages/rrdom/test/events/mutation.event.ts @@ -0,0 +1,284 @@ +import { EventType, eventWithTime, IncrementalSource } from 'rrweb/src/types'; + +const now = Date.now(); + +export const events: eventWithTime[] = [ + { + type: EventType.DomContentLoaded, + data: {}, + timestamp: now, + }, + { + type: EventType.Load, + data: {}, + timestamp: now + 100, + }, + { + type: EventType.Meta, + data: { href: 'http://localhost', width: 1512, height: 395 }, + timestamp: now + 100, + }, + { + type: EventType.FullSnapshot, + data: { + node: { + type: 0, + childNodes: [ + { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'meta', + attributes: { charset: 'utf-8' }, + childNodes: [], + id: 4, + }, + { type: 3, textContent: ' \n ', id: 5 }, + ], + id: 3, + }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { type: 3, textContent: '\n ', id: 16 }, + { + type: 2, + tagName: 'div', + attributes: { id: 'container' }, + childNodes: [{ type: 3, textContent: '\nempty\n', id: 18 }], + id: 17, + }, + { type: 3, textContent: '\n \n ', id: 19 }, + ], + id: 6, + }, + ], + id: 2, + }, + ], + compatMode: 'BackCompat', + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: now + 100, + }, + // mutation that adds five div elements + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 17, id: 18 }], + adds: [ + { + parentId: 17, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 29, + }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: '5', id: 30 }, + }, + { + parentId: 17, + nextId: 29, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 31, + }, + }, + { + parentId: 17, + nextId: 31, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 32, + }, + }, + { + parentId: 17, + nextId: 32, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 33, + }, + }, + { + parentId: 17, + nextId: 33, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 34, + }, + }, + { + parentId: 34, + nextId: null, + node: { type: 3, textContent: '1', id: 35 }, + }, + { + parentId: 33, + nextId: null, + node: { type: 3, textContent: '2', id: 36 }, + }, + { + parentId: 32, + nextId: null, + node: { type: 3, textContent: '3', id: 37 }, + }, + { + parentId: 31, + nextId: null, + node: { type: 3, textContent: '4', id: 38 }, + }, + ], + }, + timestamp: now + 1000, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [ + { parentId: 17, id: 29 }, + { parentId: 17, id: 31 }, + { parentId: 17, id: 32 }, + { parentId: 17, id: 33 }, + { parentId: 17, id: 34 }, + ], + adds: [ + { + parentId: 17, + nextId: 31, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 29, + }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: '5', id: 30 }, + }, + { + parentId: 17, + nextId: 32, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 31, + }, + }, + { + parentId: 31, + nextId: null, + node: { type: 3, textContent: '4', id: 38 }, + }, + { + parentId: 17, + nextId: 33, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 32, + }, + }, + { + parentId: 32, + nextId: null, + node: { type: 3, textContent: '3', id: 37 }, + }, + { + parentId: 17, + nextId: 34, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 33, + }, + }, + { + parentId: 33, + nextId: null, + node: { type: 3, textContent: '2', id: 36 }, + }, + { + parentId: 17, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 34, + }, + }, + { + parentId: 34, + nextId: null, + node: { type: 3, textContent: '1', id: 35 }, + }, + ], + }, + timestamp: now + 2000, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [ + { parentId: 17, id: 29 }, + { parentId: 17, id: 31 }, + { parentId: 17, id: 32 }, + { parentId: 17, id: 33 }, + { parentId: 17, id: 34 }, + ], + adds: [], + }, + timestamp: now + 3000, + }, +]; diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrdom/test/session-cutter.test.ts index 63cfe8944e..27cd16a579 100644 --- a/packages/rrdom/test/session-cutter.test.ts +++ b/packages/rrdom/test/session-cutter.test.ts @@ -1,7 +1,11 @@ import rewire from 'rewire'; import { EventType, eventWithTime } from 'rrweb/src/types'; -import { sessionCut } from '../src/trim'; -const rewiredSessionCutter = rewire('../lib/trim'); +import { RRNode, RRElement, RRIFrameElement, Mirror } from '../src/'; +import { sessionCut } from '../src/tools/session-cutter'; +import { SyncReplayer } from '../src/tools/SyncReplayer'; +import { events as mutationEvents } from './events/mutation.event'; + +const rewiredSessionCutter = rewire('../lib/session-cutter'); const getValidSortedPoints = rewiredSessionCutter.__get__( 'getValidSortedPoints', ); @@ -46,4 +50,36 @@ describe('session cutter', () => { expect(getValidSortedPoints(inputPoints, 100)).toEqual([10, 100]); expect(getValidSortedPoints(inputPoints, 300)).toEqual([10, 100, 250.5]); }); + + describe('A synchronous replayer purely built with RRDom', () => { + it('should play mutation events synchronously', () => { + const events = mutationEvents; + const replayer = new SyncReplayer(events); + replayer.play(({ event, currentTime }) => { + if (event.type === EventType.FullSnapshot) { + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot(`Full Snapshot @ ${currentTime}`); + } else if (event.type === EventType.IncrementalSnapshot) { + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot(`Incremental Snapshot @ ${currentTime}`); + } + }); + }); + }); }); + +function printRRDom(rootNode: RRNode, mirror: Mirror) { + return walk(rootNode, mirror, ''); +} +function walk(node: RRNode, mirror: Mirror, blankSpace: string) { + let printText = `${blankSpace}${mirror.getId(node)} ${node.toString()}\n`; + if (node instanceof RRElement && node.shadowRoot) + printText += walk(node.shadowRoot, mirror, blankSpace + ' '); + for (const child of node.childNodes) + printText += walk(child, mirror, blankSpace + ' '); + if (node instanceof RRIFrameElement) + printText += walk(node.contentDocument, mirror, blankSpace + ' '); + return printText; +} diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 7f2ee95431..15dd85ac4d 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -7,13 +7,13 @@ "removeComments": true, "preserveConstEnums": true, "sourceMap": true, - "rootDir": "src", "outDir": "build", "lib": ["es6", "dom"], "skipLibCheck": true, "declaration": true, "importsNotUsedAsValues": "error", - "esModuleInterop": true + "esModuleInterop": true, + "strictBindCallApply": true }, "compileOnSave": true, "exclude": ["test"], diff --git a/yarn.lock b/yarn.lock index e4bb7ec301..064b37bc85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz" @@ -301,6 +308,15 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/highlight@^7.10.4": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" + integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" @@ -502,6 +518,21 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@eslint/eslintrc@^1.2.3": version "1.2.3" resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz" @@ -517,6 +548,15 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" @@ -526,7 +566,7 @@ debug "^4.1.1" minimatch "^3.0.4" -"@humanwhocodes/object-schema@^1.2.1": +"@humanwhocodes/object-schema@^1.2.0", "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== @@ -2126,6 +2166,11 @@ dependencies: "@types/node" "*" +"@types/rewire@^2.5.28": + version "2.5.28" + resolved "https://registry.yarnpkg.com/@types/rewire/-/rewire-2.5.28.tgz#ff34de38c4269fe74e2597195d4918c25d42ebad" + integrity sha512-uD0j/AQOa5le7afuK+u+woi8jNKF1vf3DN0H7LCJhft/lNNibUr7VcAesdgtWfEKveZol3ZG1CJqwx2Bhrnl8w== + "@types/rx-core-binding@*": version "4.0.4" resolved "https://registry.npmjs.org/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz" @@ -2478,7 +2523,7 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.3.2: +acorn-jsx@^5.3.1, acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== @@ -2493,7 +2538,7 @@ acorn-walk@^8.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1: +acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -2559,11 +2604,26 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@~6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" @@ -2739,6 +2799,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" @@ -3951,7 +4016,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.3.3, debug@^4.3.4: +debug@^4.0.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4246,6 +4311,13 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" @@ -4549,6 +4621,13 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" @@ -4556,6 +4635,11 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" @@ -4566,6 +4650,52 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint@^7.32.0: + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + eslint@^8.15.0: version "8.15.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.15.0.tgz#fea1d55a7062da48d82600d2e0974c55612a11e9" @@ -4607,6 +4737,15 @@ eslint@^8.15.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" + espree@^9.3.2: version "9.3.2" resolved "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz" @@ -5651,6 +5790,11 @@ ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + ignore@^5.1.4: version "5.1.8" resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" @@ -7264,6 +7408,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" @@ -7528,6 +7677,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" @@ -9209,7 +9363,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.1: +progress@^2.0.0, progress@^2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -9556,7 +9710,7 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" -regexpp@^3.2.0: +regexpp@^3.1.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== @@ -9607,6 +9761,11 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-relative@^0.8.7: version "0.8.7" resolved "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz" @@ -9686,6 +9845,13 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rewire@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rewire/-/rewire-6.0.0.tgz#54f4fcda4df9928d28af1eb54a318bc51ca9aa99" + integrity sha512-7sZdz5dptqBCapJYocw9EcppLU62KMEqDLIILJnNET2iqzXHaQfaVP5SOJ06XvjX+dNIDJbzjw0ZWzrgDhtjYg== + dependencies: + eslint "^7.32.0" + rgb-regex@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz" @@ -9918,7 +10084,7 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.7: +semver@^7.2.1, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -10059,6 +10225,15 @@ slash@^3.0.0: resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" @@ -10278,6 +10453,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" @@ -10488,6 +10672,17 @@ symbol-tree@^3.2.4: resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table@^6.0.9: + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" From fcb5446d88ca204e77a7577cdc715f2d59b07df7 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 05/52] feat: add a rrdom snapshot method --- packages/rrdom/src/index.ts | 11 +- packages/rrdom/src/tools/snapshot.ts | 276 +++++++++++++++++++++ packages/rrdom/test/session-cutter.test.ts | 75 +++++- packages/rrdom/tsconfig.json | 3 +- 4 files changed, 350 insertions(+), 15 deletions(-) create mode 100644 packages/rrdom/src/tools/snapshot.ts diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index c81f6af698..29d4662a7b 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -136,7 +136,7 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) { } } -export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); +export class RRDocumentType extends BaseRRDocumentTypeImpl(RRNode) {} export class RRElement extends BaseRRElementImpl(RRNode) { inputData: inputData | null = null; @@ -170,14 +170,11 @@ export class RRIFrameElement extends RRElement { } } -export const RRText = BaseRRTextImpl(RRNode); -export type RRText = typeof RRText; +export class RRText extends BaseRRTextImpl(RRNode) {} -export const RRComment = BaseRRCommentImpl(RRNode); -export type RRComment = typeof RRComment; +export class RRComment extends BaseRRCommentImpl(RRNode) {} -export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); -export type RRCDATASection = typeof RRCDATASection; +export class RRCDATASection extends BaseRRCDATASectionImpl(RRNode) {} interface RRElementTagNameMap { audio: RRMediaElement; diff --git a/packages/rrdom/src/tools/snapshot.ts b/packages/rrdom/src/tools/snapshot.ts new file mode 100644 index 0000000000..4da054a972 --- /dev/null +++ b/packages/rrdom/src/tools/snapshot.ts @@ -0,0 +1,276 @@ +import { elementNode, NodeType, serializedNode } from 'rrweb-snapshot'; +import type { serializedNodeWithId, attributes } from 'rrweb-snapshot'; +import { + Mirror, + RRDocument, + RRDocumentType, + RRElement, + RRIFrameElement, + RRNode, + RRMediaElement, + RRComment, +} from '..'; + +function serializeNode( + n: RRNode, + options: { + doc: RRDocument; + mirror: Mirror; + }, +): serializedNode | false { + const { doc, mirror } = options; + // Only record root id when document object is not the base document + const rootId = getRootId(doc, mirror); + switch (n.RRNodeType) { + case NodeType.Document: + if ((n as RRDocument).compatMode !== 'CSS1Compat') { + return { + type: NodeType.Document, + childNodes: [], + compatMode: (n as RRDocument).compatMode, // probably "BackCompat" + rootId, + }; + } else { + return { + type: NodeType.Document, + childNodes: [], + rootId, + }; + } + case NodeType.DocumentType: + return { + type: NodeType.DocumentType, + name: (n as RRDocumentType).name, + publicId: (n as RRDocumentType).publicId, + systemId: (n as RRDocumentType).systemId, + rootId, + }; + case NodeType.Element: + return serializeElementNode(n as RRElement, { + doc, + mirror, + rootId, + }); + case NodeType.Text: { + const parentTagName = n.parentNode && (n.parentNode as RRElement).tagName; + const isStyle = parentTagName === 'STYLE' ? true : undefined; + return { + type: NodeType.Text, + textContent: n.textContent || '', + isStyle, + rootId, + }; + } + case NodeType.CDATA: + return { + type: NodeType.CDATA, + textContent: '', + rootId, + }; + case NodeType.Comment: + return { + type: NodeType.Comment, + textContent: (n as RRComment).textContent || '', + rootId, + }; + default: + return false; + } +} + +function getRootId(doc: RRDocument, mirror: Mirror): number | undefined { + if (!mirror.hasNode(doc)) return undefined; + const docId = mirror.getId(doc); + return docId === 1 ? undefined : docId; +} + +function getValidTagName(element: RRElement): string { + if (element instanceof HTMLFormElement) { + return 'form'; + } + + const processedTagName = element.tagName.toLowerCase().trim(); + const tagNameRegex = new RegExp('[^a-z0-9-_:]'); + if (tagNameRegex.test(processedTagName)) { + // if the tag name is odd and we cannot extract + // anything from the string, then we return a + // generic div + return 'div'; + } + + return processedTagName; +} + +function serializeElementNode( + n: RRElement, + options: { + doc: RRDocument; + mirror: Mirror; + rootId: number | undefined; + }, +): serializedNode | false { + const { mirror, rootId } = options; + const tagName = getValidTagName(n); + const attributes: attributes = n.attributes; + // media elements + if (tagName === 'audio' || tagName === 'video') { + attributes.rr_mediaState = (n as RRMediaElement).paused + ? 'paused' + : 'played'; + attributes.rr_mediaCurrentTime = (n as RRMediaElement).currentTime || ''; + } + if (n.scrollLeft) { + attributes.rr_scrollLeft = n.scrollLeft; + } + if (n.scrollTop) { + attributes.rr_scrollTop = n.scrollTop; + } + + if (tagName == 'iframe' && !(n as RRIFrameElement).contentDocument) { + // we can't record it directly as we can't see into it + // preserve the src attribute so a decision can be taken at replay time + attributes.rr_src = attributes.src; + } + + const meta = mirror.getMeta(n); + const result: serializedNode = { + type: NodeType.Element, + tagName, + attributes, + childNodes: [], + isSVG: (meta as elementNode).isSVG, + rootId, + }; + if ((meta as elementNode).needBlock !== undefined) + result.needBlock = (meta as elementNode).needBlock; + return result; +} + +export function serializeNodeWithId( + n: RRNode, + options: { + doc: RRDocument; + mirror: Mirror; + skipChild: boolean; + onSerialize?: (n: RRNode) => unknown; + onIframeLoad?: ( + iframeNode: RRIFrameElement, + node: serializedNodeWithId, + ) => unknown; + isShadowDom?: boolean; + }, +): serializedNodeWithId | null { + const { + doc, + mirror, + skipChild = false, + onSerialize, + onIframeLoad, + isShadowDom = false, + } = options; + const oldMeta = mirror.getMeta(n); + // This node doesn't exist in the original web page. + if (!oldMeta || oldMeta.id < 0) return null; + + const _serializedNode = serializeNode(n, { + doc, + mirror, + }); + if (!_serializedNode) { + // TODO: dev only + console.warn(n, 'not serialized'); + return null; + } + const serializedNode = Object.assign(_serializedNode, { id: oldMeta.id }); + + if (onSerialize) { + onSerialize(n); + } + + const recordChild = !skipChild; + + if ( + (serializedNode.type === NodeType.Document || + serializedNode.type === NodeType.Element) && + recordChild + ) { + const bypassOptions = { + doc, + mirror, + skipChild, + onSerialize, + onIframeLoad, + isShadowDom, + }; + for (const childN of Array.from(n.childNodes)) { + const serializedChildNode = serializeNodeWithId(childN, bypassOptions); + if (serializedChildNode) { + serializedNode.childNodes.push(serializedChildNode); + } + } + + if (n.RRNodeType === NodeType.Element && (n as RRElement).shadowRoot) { + serializedNode.isShadowHost = true; + const shadowRoot = (n as RRElement).shadowRoot; + if (shadowRoot) { + bypassOptions.isShadowDom = true; + for (const childN of Array.from(shadowRoot.childNodes)) { + const serializedChildNode = serializeNodeWithId( + childN, + bypassOptions, + ); + if (serializedChildNode) { + serializedChildNode.isShadow = true; + serializedNode.childNodes.push(serializedChildNode); + } + } + } + } + } + + if (isShadowDom) serializedNode.isShadow = true; + + if ( + serializedNode.type === NodeType.Element && + serializedNode.tagName === 'iframe' + ) { + const iframeDoc = (n as RRIFrameElement).contentDocument; + if (iframeDoc) { + const serializedIframeNode = serializeNodeWithId(iframeDoc, { + doc: iframeDoc, + mirror, + skipChild: false, + onSerialize, + onIframeLoad, + }); + if (serializedIframeNode && onIframeLoad) + onIframeLoad(n as RRIFrameElement, serializedIframeNode); + } + } + + return serializedNode; +} + +export function snapshot( + n: RRDocument, + options?: { + mirror?: Mirror; + skipChild?: boolean; + onSerialize?: (n: RRNode) => unknown; + onIframeLoad?: ( + iframeNode: RRIFrameElement, + node: serializedNodeWithId, + ) => unknown; + }, +): serializedNodeWithId | null { + const { mirror = new Mirror(), onSerialize, onIframeLoad } = options || {}; + return serializeNodeWithId(n, { + doc: n, + mirror, + skipChild: false, + onSerialize, + onIframeLoad, + }); +} + +export default snapshot; diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrdom/test/session-cutter.test.ts index 27cd16a579..4b3e063130 100644 --- a/packages/rrdom/test/session-cutter.test.ts +++ b/packages/rrdom/test/session-cutter.test.ts @@ -1,14 +1,36 @@ +/** + * @jest-environment jsdom + */ import rewire from 'rewire'; +import path from 'path'; +import fs from 'fs'; import { EventType, eventWithTime } from 'rrweb/src/types'; -import { RRNode, RRElement, RRIFrameElement, Mirror } from '../src/'; +import { + createMirror, + snapshot, + serializedNodeWithId, + NodeType, + elementNode, + documentNode, +} from 'rrweb-snapshot'; +import { + RRNode, + RRElement, + RRIFrameElement, + RRDocument, + buildFromDom, + Mirror as RRDomMirror, +} from '../src/'; import { sessionCut } from '../src/tools/session-cutter'; +import { snapshot as RRDomSnapshot } from '../src/tools/snapshot'; import { SyncReplayer } from '../src/tools/SyncReplayer'; import { events as mutationEvents } from './events/mutation.event'; const rewiredSessionCutter = rewire('../lib/session-cutter'); -const getValidSortedPoints = rewiredSessionCutter.__get__( - 'getValidSortedPoints', -); +const getValidSortedPoints: ( + points: number[], + totalTime: number, +) => number[] = rewiredSessionCutter.__get__('getValidSortedPoints'); describe('session cutter', () => { it('should return the same events if the events length is too short', () => { @@ -68,12 +90,36 @@ describe('session cutter', () => { }); }); }); + + describe('Build full snapshot events from RRDom', () => { + it("should build full snapshot events from RRDom's mirror: main.html", () => { + document.write(getHtml('main.html')); + const rrdom = new RRDocument(); + const mirror = createMirror(); + // the full snapshot that is built on jsdom + const originalSnapshot = snapshot(document, { mirror }); + if (originalSnapshot) snapshotFilter(originalSnapshot); + // Create a RRDom according to the jsdom (real dom). + buildFromDom(document, mirror, rrdom); + + const newFullSnapshot = RRDomSnapshot(rrdom, { + mirror: rrdom.mirror, + }); + if (newFullSnapshot) snapshotFilter(newFullSnapshot); + expect(newFullSnapshot).toEqual(originalSnapshot); + }); + }); }); -function printRRDom(rootNode: RRNode, mirror: Mirror) { +function getHtml(fileName: string) { + const filePath = path.resolve(__dirname, `./html/${fileName}`); + return fs.readFileSync(filePath, 'utf8'); +} + +function printRRDom(rootNode: RRNode, mirror: RRDomMirror) { return walk(rootNode, mirror, ''); } -function walk(node: RRNode, mirror: Mirror, blankSpace: string) { +function walk(node: RRNode, mirror: RRDomMirror, blankSpace: string) { let printText = `${blankSpace}${mirror.getId(node)} ${node.toString()}\n`; if (node instanceof RRElement && node.shadowRoot) printText += walk(node.shadowRoot, mirror, blankSpace + ' '); @@ -83,3 +129,20 @@ function walk(node: RRNode, mirror: Mirror, blankSpace: string) { printText += walk(node.contentDocument, mirror, blankSpace + ' '); return printText; } + +/** + * Some properties in the snapshot shouldn't be checked. + * 1. css styles' format are different. + * 2. href and src attributes are different. + */ +function snapshotFilter(n: serializedNodeWithId) { + if (n.type === NodeType.Element) { + delete n.attributes['href']; + delete n.attributes['src']; + } else if (n.type === NodeType.Text && n.isStyle) n.textContent = ''; + if ( + [NodeType.Document, NodeType.Element].includes(n.type) && + (n as documentNode | elementNode).childNodes + ) + for (const child of (n as elementNode).childNodes) snapshotFilter(child); +} diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 15dd85ac4d..8df6e9a058 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -16,6 +16,5 @@ "strictBindCallApply": true }, "compileOnSave": true, - "exclude": ["test"], - "include": ["src", "../rrweb/src/record/workers/workers.d.ts"] + "include": ["src", "test", "../rrweb/src/record/workers/workers.d.ts"] } From b6a64184259321144acea4e96c56fa335cef3d66 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 06/52] feat: finish a prototype of session cutter --- packages/rrdom/src/tools/SyncReplayer.ts | 36 +---------- packages/rrdom/src/tools/session-cutter.ts | 47 ++++++++++++--- .../__snapshots__/session-cutter.test.ts.snap | 59 +++++++++++++++++++ packages/rrdom/test/events/mutation.event.ts | 4 +- packages/rrdom/test/session-cutter.test.ts | 54 ++++++++++++++++- 5 files changed, 156 insertions(+), 44 deletions(-) diff --git a/packages/rrdom/src/tools/SyncReplayer.ts b/packages/rrdom/src/tools/SyncReplayer.ts index df3c6926a6..92c35b5a23 100644 --- a/packages/rrdom/src/tools/SyncReplayer.ts +++ b/packages/rrdom/src/tools/SyncReplayer.ts @@ -76,6 +76,8 @@ export class SyncReplayer { public events: eventWithTime[]; + public latestMetaEvent: eventWithTime | null = null; + public unhandledEvents: eventWithTime[] = []; private currentTime = 0; @@ -90,8 +92,6 @@ export class SyncReplayer { private mirror: RRDOMMirror = this.virtualDom.mirror; - private firstFullSnapshot: eventWithTime | true | null = null; - private newDocumentQueue: addedNodeMutation[] = []; constructor( @@ -119,16 +119,12 @@ export class SyncReplayer { this.config = Object.assign({}, defaultConfig, config); this.emitter.on(ReplayerEvents.PlayBack, () => { - this.firstFullSnapshot = null; this.mirror.reset(); }); // rebuild first full snapshot as the poster of the player // maybe we can cache it for performance optimization const firstMeta = this.events.find((e) => e.type === EventType.Meta); - const firstFullsnapshot = this.events.find( - (e) => e.type === EventType.FullSnapshot, - ); if (firstMeta) { const { width, height } = firstMeta.data as metaEvent['data']; setTimeout(() => { @@ -138,23 +134,6 @@ export class SyncReplayer { }); }, 0); } - if (firstFullsnapshot) { - setTimeout(() => { - // when something has been played, there is no need to rebuild poster - if (this.firstFullSnapshot) { - // true if any other fullSnapshot has been executed by Timer already - return; - } - this.firstFullSnapshot = firstFullsnapshot; - this.rebuildFullSnapshot( - firstFullsnapshot as fullSnapshotEvent & { timestamp: number }, - ); - const initialOffset = (firstFullsnapshot as fullSnapshotEvent).data - .initialOffset; - this.virtualDom.scrollTop = initialOffset.top; - this.virtualDom.scrollLeft = initialOffset.left; - }, 1); - } } public on(event: string, handler: Handler) { @@ -248,19 +227,10 @@ export class SyncReplayer { width: event.data.width, height: event.data.height, }); + this.latestMetaEvent = event; break; case EventType.FullSnapshot: castFn = () => { - if (this.firstFullSnapshot) { - if (this.firstFullSnapshot === event) { - // we've already built this exact FullSnapshot when the player was mounted, and haven't built any other FullSnapshot since - this.firstFullSnapshot = true; // forget as we might need to re-execute this FullSnapshot later e.g. to rebuild after scrubbing - return; - } - } else { - // Timer (requestAnimationFrame) can be faster than setTimeout(..., 1) - this.firstFullSnapshot = true; - } this.rebuildFullSnapshot(event); this.virtualDom.scrollTop = event.data.initialOffset.top; this.virtualDom.scrollLeft = event.data.initialOffset.left; diff --git a/packages/rrdom/src/tools/session-cutter.ts b/packages/rrdom/src/tools/session-cutter.ts index e7b0851e50..327b729887 100644 --- a/packages/rrdom/src/tools/session-cutter.ts +++ b/packages/rrdom/src/tools/session-cutter.ts @@ -1,13 +1,17 @@ -import type { eventWithTime } from 'rrweb/src/types'; +import { EventType, eventWithTime } from 'rrweb/src/types'; +import snapshot from './snapshot'; import { SyncReplayer } from './SyncReplayer'; type CutterConfig = { points: number[]; }; -export function sessionCut(events: eventWithTime[], config: CutterConfig) { +export function sessionCut( + events: eventWithTime[], + config: CutterConfig, +): eventWithTime[][] { // Events length is too short so that cutting process is not needed. - if (events.length < 2) return events; + if (events.length < 2) return [events]; const { points } = config; - if (!points || points.length == 0) return events; + if (!points || points.length == 0) return [events]; events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); const totalTime = events[events.length - 1].timestamp - events[0].timestamp; @@ -23,7 +27,7 @@ export function sessionCut(events: eventWithTime[], config: CutterConfig) { ); replayer.play(({ index, event }) => { if ( - event.timestamp < validSortedTimestamp[cutPointIndex] && + event.timestamp <= validSortedTimestamp[cutPointIndex] && index + 1 < events.length ) { const nextEvent = events[index + 1]; @@ -42,6 +46,7 @@ export function sessionCut(events: eventWithTime[], config: CutterConfig) { const result = cutEvents( events.slice(index + 1), replayer, + event.timestamp, nextCutTimestamp, ); results.push(result); @@ -55,10 +60,38 @@ export function sessionCut(events: eventWithTime[], config: CutterConfig) { function cutEvents( events: eventWithTime[], replayer: SyncReplayer, + currentTimestamp: number, endTimestamp: number, ) { - // TODO - return []; + const result: eventWithTime[] = []; + if (replayer.latestMetaEvent) { + const metaEvent = replayer.latestMetaEvent; + metaEvent.timestamp = currentTimestamp; + result.push(metaEvent); + } + result.push( + ...replayer.unhandledEvents.map((e) => { + e.timestamp = currentTimestamp + 10; + return e; + }), + ); + const fullSnapshot = snapshot(replayer.virtualDom, { + mirror: replayer.getMirror(), + }); + if (fullSnapshot) + result.push({ + type: EventType.FullSnapshot, + data: { + node: fullSnapshot, + initialOffset: { + top: 0, + left: 0, + }, + }, + timestamp: currentTimestamp, + }); + result.push(...events.filter((event) => event.timestamp <= endTimestamp)); + return result; } function getValidSortedPoints(points: number[], totalTime: number) { diff --git a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap index 1cff10690e..7d83f77a5a 100644 --- a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap @@ -76,3 +76,62 @@ exports[`session cutter A synchronous replayer purely built with RRDom should pl 19 RRText text=\\"\\\\n \\\\n \\" " `; + +exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 1000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 34 DIV + 35 RRText text=\\"1\\" + 33 DIV + 36 RRText text=\\"2\\" + 32 DIV + 37 RRText text=\\"3\\" + 31 DIV + 38 RRText text=\\"4\\" + 29 DIV + 30 RRText text=\\"5\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 2000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 29 DIV + 30 RRText text=\\"5\\" + 31 DIV + 38 RRText text=\\"4\\" + 32 DIV + 37 RRText text=\\"3\\" + 33 DIV + 36 RRText text=\\"2\\" + 34 DIV + 35 RRText text=\\"1\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 3000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; diff --git a/packages/rrdom/test/events/mutation.event.ts b/packages/rrdom/test/events/mutation.event.ts index fcd4934f2f..2d1b84b8bd 100644 --- a/packages/rrdom/test/events/mutation.event.ts +++ b/packages/rrdom/test/events/mutation.event.ts @@ -73,7 +73,7 @@ export const events: eventWithTime[] = [ }, timestamp: now + 100, }, - // mutation that adds five div elements + // mutation that adds five div elements at 1000ms { type: EventType.IncrementalSnapshot, data: { @@ -166,6 +166,7 @@ export const events: eventWithTime[] = [ }, timestamp: now + 1000, }, + // mutation that reverses the order of five div elements at 2000ms { type: EventType.IncrementalSnapshot, data: { @@ -264,6 +265,7 @@ export const events: eventWithTime[] = [ }, timestamp: now + 2000, }, + // mutation that removes five div elements at 3000ms { type: EventType.IncrementalSnapshot, data: { diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrdom/test/session-cutter.test.ts index 4b3e063130..9da174c3d4 100644 --- a/packages/rrdom/test/session-cutter.test.ts +++ b/packages/rrdom/test/session-cutter.test.ts @@ -36,7 +36,7 @@ describe('session cutter', () => { it('should return the same events if the events length is too short', () => { const events1: eventWithTime[] = []; const config = { points: [10] }; - expect(sessionCut(events1, config)).toEqual(events1); + expect(sessionCut(events1, config)).toEqual([events1]); const events2: eventWithTime[] = [ { @@ -45,7 +45,7 @@ describe('session cutter', () => { timestamp: 1, } as eventWithTime, ]; - expect(sessionCut(events2, config)).toEqual(events2); + expect(sessionCut(events2, config)).toEqual([events2]); }); it('should return the same events if the points length is 0', () => { @@ -62,7 +62,7 @@ describe('session cutter', () => { } as eventWithTime, ]; const config = { points: [] }; - expect(sessionCut(events, config)).toEqual(events); + expect(sessionCut(events, config)).toEqual([events]); }); it('should sort and validate cutting points array', () => { @@ -109,6 +109,54 @@ describe('session cutter', () => { expect(newFullSnapshot).toEqual(originalSnapshot); }); }); + + describe('Cut the session events from several time points', () => { + it('should cut the simplest mutation events', () => { + const events = mutationEvents; + const result = sessionCut(events, { points: [1000, 2000] }); + expect(result).toHaveLength(3); + + // all events before 1000ms + const sessionBefore1s = result[0]; + const cutPoint1Length = 5; + expect(sessionBefore1s).toHaveLength(cutPoint1Length); + // These events are directly sliced from the original events. + expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); + + // all events between 1000ms and 2000ms + const sessionBetween1s2s = result[1]; + expect(sessionBetween1s2s).toHaveLength(3); + expect(sessionBetween1s2s[0].type).toEqual(EventType.Meta); + expect(sessionBetween1s2s[1].type).toEqual(EventType.FullSnapshot); + expect(sessionBetween1s2s[2].type).toEqual(EventType.IncrementalSnapshot); + let replayer = new SyncReplayer(sessionBetween1s2s.slice(0, 2)); // only play meta and full snapshot events + replayer.play(); + // screenshot at 1000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 1000ms'); + + // all events after 2000ms + const sessionAfter2s = result[2]; + expect(sessionAfter2s).toHaveLength(3); + expect(sessionAfter2s[0].type).toEqual(EventType.Meta); + expect(sessionAfter2s[1].type).toEqual(EventType.FullSnapshot); + expect(sessionAfter2s[2].type).toEqual(EventType.IncrementalSnapshot); + replayer = new SyncReplayer(sessionAfter2s); + replayer.play(({ index }) => { + if (index === 1) + // full snapshot + // screen shot at 2000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 2000ms'); + }); + // screen shot at 3000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 3000ms'); + }); + }); }); function getHtml(fileName: string) { From 2a7d76973768c793a56c7ca309641cdaf41f897e Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 4 Jul 2022 01:52:48 +1000 Subject: [PATCH 07/52] fix: some inline css styles can't be generated by the cutter --- packages/rrdom/src/tools/session-cutter.ts | 3 +- packages/rrdom/src/tools/snapshot.ts | 11 +- .../__snapshots__/session-cutter.test.ts.snap | 23 +++ .../rrdom/test/events/inline-style.event.ts | 136 ++++++++++++++++++ packages/rrdom/test/session-cutter.test.ts | 28 +++- 5 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 packages/rrdom/test/events/inline-style.event.ts diff --git a/packages/rrdom/src/tools/session-cutter.ts b/packages/rrdom/src/tools/session-cutter.ts index 327b729887..78867d38cb 100644 --- a/packages/rrdom/src/tools/session-cutter.ts +++ b/packages/rrdom/src/tools/session-cutter.ts @@ -4,6 +4,7 @@ import { SyncReplayer } from './SyncReplayer'; type CutterConfig = { points: number[]; }; + export function sessionCut( events: eventWithTime[], config: CutterConfig, @@ -98,7 +99,7 @@ function getValidSortedPoints(points: number[], totalTime: number) { const validSortedPoints = []; for (let i = 0; i < points.length; i++) { const point = points[i]; - if (point <= 0 || point > totalTime) continue; + if (point <= 0 || point >= totalTime) continue; validSortedPoints.push(point); } return validSortedPoints.sort(); diff --git a/packages/rrdom/src/tools/snapshot.ts b/packages/rrdom/src/tools/snapshot.ts index 4da054a972..2c355fafd8 100644 --- a/packages/rrdom/src/tools/snapshot.ts +++ b/packages/rrdom/src/tools/snapshot.ts @@ -85,10 +85,6 @@ function getRootId(doc: RRDocument, mirror: Mirror): number | undefined { } function getValidTagName(element: RRElement): string { - if (element instanceof HTMLFormElement) { - return 'form'; - } - const processedTagName = element.tagName.toLowerCase().trim(); const tagNameRegex = new RegExp('[^a-z0-9-_:]'); if (tagNameRegex.test(processedTagName)) { @@ -118,6 +114,13 @@ function serializeElementNode( ? 'paused' : 'played'; attributes.rr_mediaCurrentTime = (n as RRMediaElement).currentTime || ''; + } else if ( + (tagName === 'link' || tagName === 'style') && + n.textContent !== '' + ) { + // the child text is inserted and untracked by the rrweb replayer + if (n.childNodes[0] && mirror.getId(n.childNodes[0]) < 0) + attributes._cssText = n.textContent; } if (n.scrollLeft) { attributes.rr_scrollLeft = n.scrollLeft; diff --git a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap index 7d83f77a5a..da174e7f90 100644 --- a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap @@ -135,3 +135,26 @@ exports[`session cutter Cut the session events from several time points should c 19 RRText text=\\"\\\\n \\\\n \\" " `; + +exports[`session cutter should cut events with inline styles: screenshot at 1000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 RRText text=\\"\\\\n \\" + 5 META charset=\\"utf-8\\" + 6 RRText text=\\"\\\\n \\" + 7 STYLE + -1 RRText text=\\"#root { background: yellow; width: 10px; height: 10px; }\\" + 8 RRText text=\\"\\\\n \\" + 9 STYLE + -1 RRText text=\\".block { width: 20px; height: 20px; background: red; }\\" + 10 RRText text=\\"\\\\n \\" + 11 RRText text=\\"\\\\n \\" + 12 BODY + 13 RRText text=\\"\\\\n \\" + 14 DIV id=\\"root\\" + 15 RRText text=\\" \\\\n \\" + 16 RRText text=\\"\\\\n \\" + 17 DIV class=\\"block\\" +" +`; diff --git a/packages/rrdom/test/events/inline-style.event.ts b/packages/rrdom/test/events/inline-style.event.ts new file mode 100644 index 0000000000..b5095bb346 --- /dev/null +++ b/packages/rrdom/test/events/inline-style.event.ts @@ -0,0 +1,136 @@ +import { EventType, eventWithTime, IncrementalSource } from 'rrweb/src/types'; + +const now = Date.now(); + +export const events: eventWithTime[] = [ + { + type: EventType.DomContentLoaded, + data: {}, + timestamp: now, + }, + { + type: EventType.Load, + data: {}, + timestamp: now + 100, + }, + { + type: EventType.Meta, + data: { href: 'http://localhost', width: 1512, height: 395 }, + timestamp: now + 100, + }, + // a full snapshot contains a link element and a style element contains inline css styles + { + type: EventType.FullSnapshot, + data: { + node: { + type: 0, + childNodes: [ + { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [ + { type: 3, textContent: '\n ', id: 4 }, + { + type: 2, + tagName: 'meta', + attributes: { charset: 'utf-8' }, + childNodes: [], + id: 5, + }, + { type: 3, textContent: '\n ', id: 6 }, + // Inlined _cssText is downloaded from a remote css file. + { + type: 2, + tagName: 'link', + attributes: { + _cssText: + '#root { background: yellow; width: 10px; height: 10px; }', + }, + childNodes: [], + id: 7, + }, + { type: 3, textContent: '\n ', id: 8 }, + // These styles are inserted into style element through javascript. + { + type: 2, + tagName: 'style', + attributes: { + _cssText: + '.block { width: 20px; height: 20px; background: red; }', + }, + childNodes: [], + id: 9, + }, + { type: 3, textContent: '\n ', id: 10 }, + ], + id: 3, + }, + { type: 3, textContent: '\n ', id: 11 }, + { + type: 2, + tagName: 'body', + childNodes: [ + { type: 3, textContent: '\n ', id: 13 }, + { + type: 2, + tagName: 'div', + attributes: { id: 'root' }, + childNodes: [ + { type: 3, textContent: ' \n ', id: 15 }, + ], + id: 14, + }, + { type: 3, textContent: '\n ', id: 16 }, + { + type: 2, + tagName: 'div', + attributes: { class: 'block' }, + childNodes: [], + id: 17, + }, + { type: 3, textContent: '\n\n', id: 18 }, + ], + id: 12, + }, + ], + id: 2, + }, + ], + compatMode: 'BackCompat', + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: now + 200, + }, + // mutation that adds a div element at 1000ms + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 12, id: 18 }], + adds: [], + }, + timestamp: now + 1000, + }, + // a placeholder event to extend the duration of the whole session + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseInteraction, + type: 2, + id: 15, + x: 190, + y: 19, + }, + timestamp: now + 1500, + }, +]; diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrdom/test/session-cutter.test.ts index 9da174c3d4..66e56e6764 100644 --- a/packages/rrdom/test/session-cutter.test.ts +++ b/packages/rrdom/test/session-cutter.test.ts @@ -25,6 +25,7 @@ import { sessionCut } from '../src/tools/session-cutter'; import { snapshot as RRDomSnapshot } from '../src/tools/snapshot'; import { SyncReplayer } from '../src/tools/SyncReplayer'; import { events as mutationEvents } from './events/mutation.event'; +import { events as inlineStyleEvents } from './events/inline-style.event'; const rewiredSessionCutter = rewire('../lib/session-cutter'); const getValidSortedPoints: ( @@ -68,8 +69,9 @@ describe('session cutter', () => { it('should sort and validate cutting points array', () => { const inputPoints = [10, 250.5, -10, -1, 0, 100]; expect(getValidSortedPoints([], 100)).toEqual([]); - expect(getValidSortedPoints(inputPoints, 10)).toEqual([10]); - expect(getValidSortedPoints(inputPoints, 100)).toEqual([10, 100]); + expect(getValidSortedPoints(inputPoints, 11)).toEqual([10]); + expect(getValidSortedPoints(inputPoints, 10)).toEqual([]); + expect(getValidSortedPoints(inputPoints, 100)).toEqual([10]); expect(getValidSortedPoints(inputPoints, 300)).toEqual([10, 100, 250.5]); }); @@ -157,6 +159,28 @@ describe('session cutter', () => { ).toMatchSnapshot('screenshot at 3000ms'); }); }); + + it('should cut events with inline styles', () => { + const events = inlineStyleEvents; + const result = sessionCut(events, { points: [1000] }); + expect(result).toHaveLength(2); + // all events before 1000ms + const sessionBefore1s = result[0]; + const cutPoint1Length = 5; + expect(sessionBefore1s).toHaveLength(cutPoint1Length); + // These events are directly sliced from the original events. + expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); + + // all events after 1000ms + const sessionAfter1s = result[1]; + expect(sessionAfter1s).toHaveLength(3); + const replayer = new SyncReplayer(sessionAfter1s.slice(0, 2)); // only play meta and full snapshot events + replayer.play(); + // screenshot at 1000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 1000ms'); + }); }); function getHtml(fileName: string) { From 6424d9b1b4116a2fb2026bc3422a3f78b5e8f758 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 6 Jul 2022 21:30:11 +1000 Subject: [PATCH 08/52] refactor: move session-cutter from rrdom to rrweb-cutter --- .vscode/rrweb-monorepo.code-workspace | 4 + packages/rrdom/rollup.config.js | 5 - packages/rrdom/test/virtual-dom.test.ts | 31 +-- packages/rrdom/tsconfig.json | 3 +- packages/rrweb-cutter/.eslintignore | 3 + packages/rrweb-cutter/README.md | 0 packages/rrweb-cutter/jest.config.js | 5 + packages/rrweb-cutter/package.json | 41 ++++ .../src/index.ts} | 7 +- .../tools => rrweb-cutter/src}/snapshot.ts | 12 +- .../__snapshots__/session-cutter.test.ts.snap | 78 -------- .../test/events/inline-style.event.ts | 0 .../test/events/mutation.event.ts | 0 packages/rrweb-cutter/test/html/main.html | 44 +++++ .../test/session-cutter.test.ts | 60 +----- packages/rrweb-cutter/tsconfig.json | 24 +++ packages/rrweb-cutter/vite.config.ts | 17 ++ packages/rrweb/rollup.config.js | 12 ++ packages/rrweb/src/index.ts | 2 + .../src/replay/sync-replayer.ts} | 18 +- packages/rrweb/typings/index.d.ts | 3 +- .../rrweb/typings/replay/sync-replayer.d.ts | 46 +++++ yarn.lock | 182 +++++++++++++++++- 23 files changed, 413 insertions(+), 184 deletions(-) create mode 100644 packages/rrweb-cutter/.eslintignore create mode 100644 packages/rrweb-cutter/README.md create mode 100644 packages/rrweb-cutter/jest.config.js create mode 100644 packages/rrweb-cutter/package.json rename packages/{rrdom/src/tools/session-cutter.ts => rrweb-cutter/src/index.ts} (93%) rename packages/{rrdom/src/tools => rrweb-cutter/src}/snapshot.ts (99%) rename packages/{rrdom => rrweb-cutter}/test/__snapshots__/session-cutter.test.ts.snap (52%) rename packages/{rrdom => rrweb-cutter}/test/events/inline-style.event.ts (100%) rename packages/{rrdom => rrweb-cutter}/test/events/mutation.event.ts (100%) create mode 100644 packages/rrweb-cutter/test/html/main.html rename packages/{rrdom => rrweb-cutter}/test/session-cutter.test.ts (75%) create mode 100644 packages/rrweb-cutter/tsconfig.json create mode 100644 packages/rrweb-cutter/vite.config.ts rename packages/{rrdom/src/tools/SyncReplayer.ts => rrweb/src/replay/sync-replayer.ts} (99%) create mode 100644 packages/rrweb/typings/replay/sync-replayer.d.ts diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 896a62e5d9..608e5304a7 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -23,6 +23,10 @@ { "name": "rrweb-snapshot (package)", "path": "../packages/rrweb-snapshot" + }, + { + "name": "rrweb-cutter (package)", + "path": "../packages/rrweb-cutter" } ], "settings": { diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index ddb0e69cca..5bd346673f 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -27,11 +27,6 @@ const baseConfigs = [ name: pkg.name, path: pkg.name, }, - { - input: './src/tools/session-cutter.ts', - name: pkg.name, - path: 'session-cutter', - }, ]; let configs = []; diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index ef9e940b49..be20fc4444 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -30,27 +30,6 @@ import { BaseRRNode as RRNode, } from '../src'; -const printRRDomCode = ` -/** - * Print the RRDom as a string. - * @param rootNode the root node of the RRDom tree - * @returns printed string - */ -function printRRDom(rootNode, mirror) { - return walk(rootNode, mirror, ''); -} -function walk(node, mirror, blankSpace) { - let printText = \`\${blankSpace}\${mirror.getId(node)} \${node.toString()}\n\`; - if(node instanceof rrdom.RRElement && node.shadowRoot) - printText += walk(node.shadowRoot, mirror, blankSpace + ' '); - for (const child of node.childNodes) - printText += walk(child, mirror, blankSpace + ' '); - if (node instanceof rrdom.RRIFrameElement) - printText += walk(node.contentDocument, mirror, blankSpace + ' '); - return printText; -} -`; - describe('RRDocument for browser environment', () => { let mirror: Mirror; beforeEach(() => { @@ -241,7 +220,7 @@ describe('RRDocument for browser environment', () => { beforeEach(async () => { page = await browser.newPage(); await page.goto('about:blank'); - await page.evaluate(code + printRRDomCode); + await page.evaluate(code); }); afterEach(async () => { @@ -252,7 +231,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - printRRDom(doc, doc.mirror); + rrdom.printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -262,7 +241,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - printRRDom(doc, doc.mirror); + rrdom.printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -272,7 +251,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - printRRDom(doc, doc.mirror); + rrdom.printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -286,7 +265,7 @@ describe('RRDocument for browser environment', () => { const doc = new rrdom.RRDocument(); rrdom.buildFromDom(docu, undefined, doc); - printRRDom(doc, doc.mirror); + rrdom.printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 8df6e9a058..252f40df7f 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "rootDir": "src", "target": "ES6", "module": "commonjs", "noImplicitAny": true, @@ -16,5 +17,5 @@ "strictBindCallApply": true }, "compileOnSave": true, - "include": ["src", "test", "../rrweb/src/record/workers/workers.d.ts"] + "include": ["src", "../rrweb/src/record/workers/workers.d.ts"] } diff --git a/packages/rrweb-cutter/.eslintignore b/packages/rrweb-cutter/.eslintignore new file mode 100644 index 0000000000..6c88efcd46 --- /dev/null +++ b/packages/rrweb-cutter/.eslintignore @@ -0,0 +1,3 @@ +vite.config.ts +jest.config.js +test \ No newline at end of file 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/jest.config.js b/packages/rrweb-cutter/jest.config.js new file mode 100644 index 0000000000..e86e13bab9 --- /dev/null +++ b/packages/rrweb-cutter/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json new file mode 100644 index 0000000000..799fe471a2 --- /dev/null +++ b/packages/rrweb-cutter/package.json @@ -0,0 +1,41 @@ +{ + "name": "rrweb-cutter", + "version": "0.1.2", + "scripts": { + "check-types": "tsc -noEmit", + "dev": "vite build --watch", + "bundle": "vite build && tsc --emitDeclarationOnly", + "test": "jest", + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" + }, + "author": "yun.feng0817@gmail.com", + "keywords": [ + "rrweb", + "rrdom", + "session cutter" + ], + "license": "MIT", + "main": "dist/index.umd.js", + "unpkg": "dist/index.iife.js", + "module": "dist/index.es.js", + "typings": "dist/types", + "files": [ + "dist" + ], + "devDependencies": { + "@types/jest": "^27.4.1", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", + "eslint": "^8.15.0", + "jest": "^27.5.1", + "ts-jest": "^27.1.3", + "typescript": "^4.7.3", + "vite": "^2.9.13" + }, + "dependencies": { + "rrdom": "^0.1.2", + "rrweb-snapshot": "^1.1.14", + "rrweb": "^1.1.3" + } +} diff --git a/packages/rrdom/src/tools/session-cutter.ts b/packages/rrweb-cutter/src/index.ts similarity index 93% rename from packages/rrdom/src/tools/session-cutter.ts rename to packages/rrweb-cutter/src/index.ts index 78867d38cb..ed6c45d4ae 100644 --- a/packages/rrdom/src/tools/session-cutter.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,6 +1,6 @@ -import { EventType, eventWithTime } from 'rrweb/src/types'; +import type { eventWithTime } from 'rrweb/typings/types'; +import { EventType, SyncReplayer } from 'rrweb'; import snapshot from './snapshot'; -import { SyncReplayer } from './SyncReplayer'; type CutterConfig = { points: number[]; }; @@ -54,6 +54,7 @@ export function sessionCut( } return cutPointIndex < validSortedTimestamp.length; } + return false; }); return results; } @@ -95,7 +96,7 @@ function cutEvents( return result; } -function getValidSortedPoints(points: number[], totalTime: number) { +export function getValidSortedPoints(points: number[], totalTime: number) { const validSortedPoints = []; for (let i = 0; i < points.length; i++) { const point = points[i]; diff --git a/packages/rrdom/src/tools/snapshot.ts b/packages/rrweb-cutter/src/snapshot.ts similarity index 99% rename from packages/rrdom/src/tools/snapshot.ts rename to packages/rrweb-cutter/src/snapshot.ts index 2c355fafd8..a7184fa2c8 100644 --- a/packages/rrdom/src/tools/snapshot.ts +++ b/packages/rrweb-cutter/src/snapshot.ts @@ -1,15 +1,15 @@ import { elementNode, NodeType, serializedNode } from 'rrweb-snapshot'; import type { serializedNodeWithId, attributes } from 'rrweb-snapshot'; -import { - Mirror, +import { Mirror } from 'rrdom'; +import type { + RRNode, + RRMediaElement, + RRElement, RRDocument, RRDocumentType, - RRElement, RRIFrameElement, - RRNode, - RRMediaElement, RRComment, -} from '..'; +} from 'rrdom'; function serializeNode( n: RRNode, diff --git a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap similarity index 52% rename from packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap rename to packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index da174e7f90..893d7b99d3 100644 --- a/packages/rrdom/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -1,82 +1,4 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Full Snapshot @ 100 1`] = ` -"1 RRDocument - -1 RRDocumentType - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 18 RRText text=\\"\\\\nempty\\\\n\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 1000 1`] = ` -"1 RRDocument - -1 RRDocumentType - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 34 DIV - 35 RRText text=\\"1\\" - 33 DIV - 36 RRText text=\\"2\\" - 32 DIV - 37 RRText text=\\"3\\" - 31 DIV - 38 RRText text=\\"4\\" - 29 DIV - 30 RRText text=\\"5\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 2000 1`] = ` -"1 RRDocument - -1 RRDocumentType - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 29 DIV - 30 RRText text=\\"5\\" - 31 DIV - 38 RRText text=\\"4\\" - 32 DIV - 37 RRText text=\\"3\\" - 33 DIV - 36 RRText text=\\"2\\" - 34 DIV - 35 RRText text=\\"1\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 3000 1`] = ` -"1 RRDocument - -1 RRDocumentType - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 1000ms 1`] = ` "1 RRDocument 2 HTML diff --git a/packages/rrdom/test/events/inline-style.event.ts b/packages/rrweb-cutter/test/events/inline-style.event.ts similarity index 100% rename from packages/rrdom/test/events/inline-style.event.ts rename to packages/rrweb-cutter/test/events/inline-style.event.ts diff --git a/packages/rrdom/test/events/mutation.event.ts b/packages/rrweb-cutter/test/events/mutation.event.ts similarity index 100% rename from packages/rrdom/test/events/mutation.event.ts rename to packages/rrweb-cutter/test/events/mutation.event.ts diff --git a/packages/rrweb-cutter/test/html/main.html b/packages/rrweb-cutter/test/html/main.html new file mode 100644 index 0000000000..40e3c169e2 --- /dev/null +++ b/packages/rrweb-cutter/test/html/main.html @@ -0,0 +1,44 @@ + + + + + + Main + + + + +

This is a h1 heading

+

This is a h1 heading with styles

+
+
+ Text 1 +
+

This is a paragraph

+ +
+ Text 2 +
+ This is an image + +
+ +
+
+ + diff --git a/packages/rrdom/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts similarity index 75% rename from packages/rrdom/test/session-cutter.test.ts rename to packages/rrweb-cutter/test/session-cutter.test.ts index 66e56e6764..de08d6cfbe 100644 --- a/packages/rrdom/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -1,10 +1,8 @@ /** * @jest-environment jsdom */ -import rewire from 'rewire'; import path from 'path'; import fs from 'fs'; -import { EventType, eventWithTime } from 'rrweb/src/types'; import { createMirror, snapshot, @@ -13,26 +11,14 @@ import { elementNode, documentNode, } from 'rrweb-snapshot'; -import { - RRNode, - RRElement, - RRIFrameElement, - RRDocument, - buildFromDom, - Mirror as RRDomMirror, -} from '../src/'; -import { sessionCut } from '../src/tools/session-cutter'; -import { snapshot as RRDomSnapshot } from '../src/tools/snapshot'; -import { SyncReplayer } from '../src/tools/SyncReplayer'; +import { EventType, SyncReplayer } from 'rrweb'; +import type { eventWithTime } from 'rrweb/typings/types'; +import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; +import { sessionCut, getValidSortedPoints } from '../src'; +import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { events as inlineStyleEvents } from './events/inline-style.event'; -const rewiredSessionCutter = rewire('../lib/session-cutter'); -const getValidSortedPoints: ( - points: number[], - totalTime: number, -) => number[] = rewiredSessionCutter.__get__('getValidSortedPoints'); - describe('session cutter', () => { it('should return the same events if the events length is too short', () => { const events1: eventWithTime[] = []; @@ -75,24 +61,6 @@ describe('session cutter', () => { expect(getValidSortedPoints(inputPoints, 300)).toEqual([10, 100, 250.5]); }); - describe('A synchronous replayer purely built with RRDom', () => { - it('should play mutation events synchronously', () => { - const events = mutationEvents; - const replayer = new SyncReplayer(events); - replayer.play(({ event, currentTime }) => { - if (event.type === EventType.FullSnapshot) { - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot(`Full Snapshot @ ${currentTime}`); - } else if (event.type === EventType.IncrementalSnapshot) { - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot(`Incremental Snapshot @ ${currentTime}`); - } - }); - }); - }); - describe('Build full snapshot events from RRDom', () => { it("should build full snapshot events from RRDom's mirror: main.html", () => { document.write(getHtml('main.html')); @@ -114,7 +82,7 @@ describe('session cutter', () => { describe('Cut the session events from several time points', () => { it('should cut the simplest mutation events', () => { - const events = mutationEvents; + const events = mutationEvents as eventWithTime[]; const result = sessionCut(events, { points: [1000, 2000] }); expect(result).toHaveLength(3); @@ -161,7 +129,7 @@ describe('session cutter', () => { }); it('should cut events with inline styles', () => { - const events = inlineStyleEvents; + const events = inlineStyleEvents as eventWithTime[]; const result = sessionCut(events, { points: [1000] }); expect(result).toHaveLength(2); // all events before 1000ms @@ -188,20 +156,6 @@ function getHtml(fileName: string) { return fs.readFileSync(filePath, 'utf8'); } -function printRRDom(rootNode: RRNode, mirror: RRDomMirror) { - return walk(rootNode, mirror, ''); -} -function walk(node: RRNode, mirror: RRDomMirror, blankSpace: string) { - let printText = `${blankSpace}${mirror.getId(node)} ${node.toString()}\n`; - if (node instanceof RRElement && node.shadowRoot) - printText += walk(node.shadowRoot, mirror, blankSpace + ' '); - for (const child of node.childNodes) - printText += walk(child, mirror, blankSpace + ' '); - if (node instanceof RRIFrameElement) - printText += walk(node.contentDocument, mirror, blankSpace + ' '); - return printText; -} - /** * Some properties in the snapshot shouldn't be checked. * 1. css styles' format are different. diff --git a/packages/rrweb-cutter/tsconfig.json b/packages/rrweb-cutter/tsconfig.json new file mode 100644 index 0000000000..5b17f7104d --- /dev/null +++ b/packages/rrweb-cutter/tsconfig.json @@ -0,0 +1,24 @@ +{ + "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": true, + "esModuleInterop": true, + "noImplicitReturns": true, + "noImplicitAny": true, + "skipLibCheck": true + }, + "compileOnSave": true, + "include": ["src", "../rrweb/src/record/workers/workers.d.ts"] +} diff --git a/packages/rrweb-cutter/vite.config.ts b/packages/rrweb-cutter/vite.config.ts new file mode 100644 index 0000000000..e0ac7fe6ef --- /dev/null +++ b/packages/rrweb-cutter/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; + +export default defineConfig({ + esbuild: { + minify: true, + }, + build: { + minify: 'terser', + sourcemap: true, + lib: { + entry: resolve(__dirname, 'src/index.ts'), + name: 'rrwebCutter', + formats: ['es', 'cjs', 'umd', 'iife'], + }, + }, +}); diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index 008767236a..29ca70cf8c 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -24,6 +24,12 @@ function toReplayPath(path) { .replace('rrweb', 'rrweb-replay'); } +function toSyncReplayPath(path) { + return path + .replace(/^([\w]+)\//, '$1/replay/') + .replace('rrweb', 'rrweb-sync-replay'); +} + function toReplayUnpackPath(path) { return path .replace(/^([\w]+)\//, '$1/replay/') @@ -71,6 +77,12 @@ const baseConfigs = [ name: 'rrwebReplay', pathFn: toReplayPath, }, + // sync-replayer only + { + input: './src/replay/sync-replayer.ts', + name: 'rrwebSyncReplay', + pathFn: toSyncReplayPath, + }, // replay and unpack { input: './src/entries/replay-unpack.ts', diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index d8e8dad3ee..46be0d3d1d 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -1,5 +1,6 @@ import record from './record'; import { Replayer } from './replay'; +import { SyncReplayer } from './replay/sync-replayer'; import { _mirror } from './utils'; import * as utils from './utils'; @@ -18,6 +19,7 @@ export { addCustomEvent, freezePage, Replayer, + SyncReplayer, _mirror as mirror, utils, }; diff --git a/packages/rrdom/src/tools/SyncReplayer.ts b/packages/rrweb/src/replay/sync-replayer.ts similarity index 99% rename from packages/rrdom/src/tools/SyncReplayer.ts rename to packages/rrweb/src/replay/sync-replayer.ts index 92c35b5a23..dd49b83e3e 100644 --- a/packages/rrdom/src/tools/SyncReplayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -7,7 +7,7 @@ import { IMirror, Mirror, } from 'rrweb-snapshot'; -import { RRDocument as BaseRRDocument, StyleRuleType } from '../index'; +import { RRDocument as BaseRRDocument, StyleRuleType } from 'rrdom'; import type { RRNode, RRElement, @@ -17,11 +17,9 @@ import type { RRCanvasElement, Mirror as RRDOMMirror, VirtualStyleRules, -} from '../index'; +} from 'rrdom'; import * as mittProxy from 'mitt'; import { - EventType, - IncrementalSource, fullSnapshotEvent, eventWithTime, playerConfig, @@ -29,24 +27,26 @@ import { addedNodeMutation, incrementalSnapshotEvent, incrementalData, - ReplayerEvents, Handler, Emitter, - MediaInteractions, metaEvent, mutationData, styleValueWithPriority, mouseMovePos, canvasEventWithTime, -} from 'rrweb/src/types'; + MediaInteractions, + ReplayerEvents, + EventType, + IncrementalSource, +} from '../types'; import { queueToResolveTrees, iterateResolveTree, hasShadowRoot, isSerializedIframe, uniqueTextMutations, -} from 'rrweb/src/utils'; -import type { Replayer } from 'rrweb/src/replay'; +} from '../utils'; +import type { Replayer } from '.'; // https://github.com/rollup/rollup/issues/1267#issuecomment-296395734 const mitt = mittProxy.default || mittProxy; diff --git a/packages/rrweb/typings/index.d.ts b/packages/rrweb/typings/index.d.ts index 6b6d18d12f..1218b043ee 100644 --- a/packages/rrweb/typings/index.d.ts +++ b/packages/rrweb/typings/index.d.ts @@ -1,8 +1,9 @@ import record from './record'; import { Replayer } from './replay'; +import { SyncReplayer } from './replay/sync-replayer'; import { _mirror } from './utils'; import * as utils from './utils'; export { EventType, IncrementalSource, MouseInteractions, ReplayerEvents, } from './types'; declare const addCustomEvent: (tag: string, payload: T) => void; declare const freezePage: () => void; -export { record, addCustomEvent, freezePage, Replayer, _mirror as mirror, utils, }; +export { record, addCustomEvent, freezePage, Replayer, SyncReplayer, _mirror as mirror, utils, }; diff --git a/packages/rrweb/typings/replay/sync-replayer.d.ts b/packages/rrweb/typings/replay/sync-replayer.d.ts new file mode 100644 index 0000000000..f2083ec035 --- /dev/null +++ b/packages/rrweb/typings/replay/sync-replayer.d.ts @@ -0,0 +1,46 @@ +import { RRDocument as BaseRRDocument } from 'rrdom'; +import type { Mirror as RRDOMMirror } from 'rrdom'; +import { eventWithTime, playerConfig, playerMetaData, Handler, mouseMovePos } from '../types'; +declare class RRDocument extends BaseRRDocument { + scrollTop: number; + scrollLeft: number; +} +export declare class SyncReplayer { + config: Partial; + virtualDom: RRDocument; + mousePos: mouseMovePos | null; + events: eventWithTime[]; + latestMetaEvent: eventWithTime | null; + unhandledEvents: eventWithTime[]; + private currentTime; + private emitter; + private legacy_missingNodeRetryMap; + private cache; + private mirror; + private newDocumentQueue; + constructor(events: Array, config?: Partial); + on(event: string, handler: Handler): this; + off(event: string, handler: Handler): this; + setConfig(config: Partial): void; + getMetaData(): playerMetaData; + getCurrentTime(): number; + getMirror(): RRDOMMirror; + play(castEventCallback?: (event: { + index: number; + event: eventWithTime; + currentTime: number; + }) => boolean | void): void; + resetCache(): void; + private getCastFn; + private rebuildFullSnapshot; + private attachDocumentToIframe; + private collectIframeAndAttachDocument; + private applyIncremental; + private applyMutation; + private legacy_resolveMissingNode; + private warnNodeNotFound; + private debugNodeNotFound; + private warn; + private debug; +} +export {}; diff --git a/yarn.lock b/yarn.lock index 064b37bc85..e2f0963afb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4429,101 +4429,227 @@ esbuild-android-64@0.14.38: resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64" integrity sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw== +esbuild-android-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz#7e6394a0e517f738641385aaf553c7e4fb6d1ae3" + integrity sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg== + esbuild-android-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz#78acc80773d16007de5219ccce544c036abd50b8" integrity sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA== +esbuild-android-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz#6877566be0f82dd5a43030c0007d06ece7f7c02f" + integrity sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g== + esbuild-darwin-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz#e02b1291f629ebdc2aa46fabfacc9aa28ff6aa46" integrity sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA== +esbuild-darwin-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz#ea3caddb707d88f844b1aa1dea5ff3b0a71ef1fd" + integrity sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA== + esbuild-darwin-arm64@0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz" integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== +esbuild-darwin-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz#4e5eaab54df66cc319b76a2ac0e8af4e6f0d9c2f" + integrity sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA== + esbuild-freebsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz#790b8786729d4aac7be17648f9ea8e0e16475b5e" integrity sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig== +esbuild-freebsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz#47b5abc7426eae66861490ffbb380acc67af5b15" + integrity sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA== + esbuild-freebsd-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz#b66340ab28c09c1098e6d9d8ff656db47d7211e6" integrity sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ== +esbuild-freebsd-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz#e8c54c8637cd44feed967ea12338b0a4da3a7b11" + integrity sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw== + esbuild-linux-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz#7927f950986fd39f0ff319e92839455912b67f70" integrity sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g== +esbuild-linux-32@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz#229cf3246de2b7937c3ac13fac622d4d7a1344c5" + integrity sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ== + esbuild-linux-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz#4893d07b229d9cfe34a2b3ce586399e73c3ac519" integrity sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q== +esbuild-linux-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz#7c0e7226c02c42aacc5656c36977493dc1e96c4f" + integrity sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug== + esbuild-linux-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz#8442402e37d0b8ae946ac616784d9c1a2041056a" integrity sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA== +esbuild-linux-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz#0af1eda474b5c6cc0cace8235b74d0cb8fcf57a7" + integrity sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw== + esbuild-linux-arm@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz#d5dbf32d38b7f79be0ec6b5fb2f9251fd9066986" integrity sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA== +esbuild-linux-arm@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz#de4d1fa6b77cdcd00e2bb43dd0801e4680f0ab52" + integrity sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw== + esbuild-linux-mips64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz#95081e42f698bbe35d8ccee0e3a237594b337eb5" integrity sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ== +esbuild-linux-mips64le@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz#822c1778495f7868e990d4da47ad7281df28fd15" + integrity sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA== + esbuild-linux-ppc64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz#dceb0a1b186f5df679618882a7990bd422089b47" integrity sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q== +esbuild-linux-ppc64le@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz#55de0a9ec4a48fedfe82a63e083164d001709447" + integrity sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ== + esbuild-linux-riscv64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz#61fb8edb75f475f9208c4a93ab2bfab63821afd2" integrity sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ== +esbuild-linux-riscv64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz#cd2b7381880b2f4b21a5a598fb673492120f18a5" + integrity sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA== + esbuild-linux-s390x@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz#34c7126a4937406bf6a5e69100185fd702d12fe0" integrity sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ== +esbuild-linux-s390x@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz#4b319eca2a5c64637fc7397ffbd9671719cdb6bf" + integrity sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g== + esbuild-netbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz#322ea9937d9e529183ee281c7996b93eb38a5d95" integrity sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q== +esbuild-netbsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz#c27cde8b5cb55dcc227943a18ab078fb98d0adbf" + integrity sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw== + esbuild-openbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz#1ca29bb7a2bf09592dcc26afdb45108f08a2cdbd" integrity sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ== +esbuild-openbsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz#af5ab2d1cb41f09064bba9465fc8bf1309150df1" + integrity sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA== + esbuild-sunos-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz#c9446f7d8ebf45093e7bb0e7045506a88540019b" integrity sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA== +esbuild-sunos-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz#db3ae20526055cf6fd5c4582676233814603ac54" + integrity sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA== + esbuild-windows-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz#f8e9b4602fd0ccbd48e5c8d117ec0ba4040f2ad1" integrity sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw== +esbuild-windows-32@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz#021ffceb0a3f83078262870da88a912293c57475" + integrity sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg== + esbuild-windows-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz#280f58e69f78535f470905ce3e43db1746518107" integrity sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw== +esbuild-windows-64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz#a4d3407b580f9faac51f61eec095fa985fb3fee4" + integrity sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA== + esbuild-windows-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz#d97e9ac0f95a4c236d9173fa9f86c983d6a53f54" integrity sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw== +esbuild-windows-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz#762c0562127d8b09bfb70a3c816460742dd82880" + integrity sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g== + +esbuild@^0.14.27: + version "0.14.48" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.48.tgz#da5d8d25cd2d940c45ea0cfecdca727f7aee2b85" + integrity sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA== + optionalDependencies: + esbuild-android-64 "0.14.48" + esbuild-android-arm64 "0.14.48" + esbuild-darwin-64 "0.14.48" + esbuild-darwin-arm64 "0.14.48" + esbuild-freebsd-64 "0.14.48" + esbuild-freebsd-arm64 "0.14.48" + esbuild-linux-32 "0.14.48" + esbuild-linux-64 "0.14.48" + esbuild-linux-arm "0.14.48" + esbuild-linux-arm64 "0.14.48" + esbuild-linux-mips64le "0.14.48" + esbuild-linux-ppc64le "0.14.48" + esbuild-linux-riscv64 "0.14.48" + esbuild-linux-s390x "0.14.48" + esbuild-netbsd-64 "0.14.48" + esbuild-openbsd-64 "0.14.48" + esbuild-sunos-64 "0.14.48" + esbuild-windows-32 "0.14.48" + esbuild-windows-64 "0.14.48" + esbuild-windows-arm64 "0.14.48" + esbuild@^0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz" @@ -6051,7 +6177,7 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" -is-core-module@^2.1.0: +is-core-module@^2.1.0, is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -8167,6 +8293,11 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" @@ -8808,7 +8939,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -9311,6 +9442,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: picocolors "^0.2.1" source-map "^0.6.1" +postcss@^8.4.13: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -9811,6 +9951,15 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19 is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.22.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@~1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" @@ -9985,6 +10134,13 @@ rollup@^2.56.3: optionalDependencies: fsevents "~2.3.2" +rollup@^2.59.0: + version "2.75.7" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9" + integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ== + optionalDependencies: + fsevents "~2.3.2" + rollup@^2.68.0: version "2.68.0" resolved "https://registry.npmjs.org/rollup/-/rollup-2.68.0.tgz" @@ -10275,6 +10431,11 @@ sort-keys@^4.0.0: dependencies: is-plain-obj "^2.0.0" +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" @@ -10618,6 +10779,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svelte-check@^1.4.0: version "1.6.0" resolved "https://registry.npmjs.org/svelte-check/-/svelte-check-1.6.0.tgz" @@ -11363,6 +11529,18 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vite@^2.9.13: + version "2.9.13" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc" + integrity sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw== + dependencies: + esbuild "^0.14.27" + postcss "^8.4.13" + resolve "^1.22.0" + rollup "^2.59.0" + optionalDependencies: + fsevents "~2.3.2" + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" From 715bc78ce7459a7cf7f00adc5d6dfe3c6ec7faa6 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 10 Jul 2022 14:41:43 +1000 Subject: [PATCH 09/52] test: add integration test case for sync replayer --- packages/rrweb/test/events/node-mutation.ts | 286 ++++++++++++++++++ .../__snapshots__/sync-replayer.test.ts.snap | 78 +++++ .../rrweb/test/replay/sync-replayer.test.ts | 22 ++ 3 files changed, 386 insertions(+) create mode 100644 packages/rrweb/test/events/node-mutation.ts create mode 100644 packages/rrweb/test/replay/__snapshots__/sync-replayer.test.ts.snap create mode 100644 packages/rrweb/test/replay/sync-replayer.test.ts diff --git a/packages/rrweb/test/events/node-mutation.ts b/packages/rrweb/test/events/node-mutation.ts new file mode 100644 index 0000000000..98a13e3b8b --- /dev/null +++ b/packages/rrweb/test/events/node-mutation.ts @@ -0,0 +1,286 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/types'; + +const now = Date.now(); + +export const events: eventWithTime[] = [ + { + type: EventType.DomContentLoaded, + data: {}, + timestamp: now, + }, + { + type: EventType.Load, + data: {}, + timestamp: now + 100, + }, + { + type: EventType.Meta, + data: { href: 'http://localhost', width: 1512, height: 395 }, + timestamp: now + 100, + }, + { + type: EventType.FullSnapshot, + data: { + node: { + type: 0, + childNodes: [ + { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'meta', + attributes: { charset: 'utf-8' }, + childNodes: [], + id: 4, + }, + { type: 3, textContent: ' \n ', id: 5 }, + ], + id: 3, + }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { type: 3, textContent: '\n ', id: 16 }, + { + type: 2, + tagName: 'div', + attributes: { id: 'container' }, + childNodes: [{ type: 3, textContent: '\nempty\n', id: 18 }], + id: 17, + }, + { type: 3, textContent: '\n \n ', id: 19 }, + ], + id: 6, + }, + ], + id: 2, + }, + ], + compatMode: 'BackCompat', + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: now + 100, + }, + // mutation that adds five div elements at 1000ms + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 17, id: 18 }], + adds: [ + { + parentId: 17, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 29, + }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: '5', id: 30 }, + }, + { + parentId: 17, + nextId: 29, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 31, + }, + }, + { + parentId: 17, + nextId: 31, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 32, + }, + }, + { + parentId: 17, + nextId: 32, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 33, + }, + }, + { + parentId: 17, + nextId: 33, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 34, + }, + }, + { + parentId: 34, + nextId: null, + node: { type: 3, textContent: '1', id: 35 }, + }, + { + parentId: 33, + nextId: null, + node: { type: 3, textContent: '2', id: 36 }, + }, + { + parentId: 32, + nextId: null, + node: { type: 3, textContent: '3', id: 37 }, + }, + { + parentId: 31, + nextId: null, + node: { type: 3, textContent: '4', id: 38 }, + }, + ], + }, + timestamp: now + 1000, + }, + // mutation that reverses the order of five div elements at 2000ms + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [ + { parentId: 17, id: 29 }, + { parentId: 17, id: 31 }, + { parentId: 17, id: 32 }, + { parentId: 17, id: 33 }, + { parentId: 17, id: 34 }, + ], + adds: [ + { + parentId: 17, + nextId: 31, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 29, + }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: '5', id: 30 }, + }, + { + parentId: 17, + nextId: 32, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 31, + }, + }, + { + parentId: 31, + nextId: null, + node: { type: 3, textContent: '4', id: 38 }, + }, + { + parentId: 17, + nextId: 33, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 32, + }, + }, + { + parentId: 32, + nextId: null, + node: { type: 3, textContent: '3', id: 37 }, + }, + { + parentId: 17, + nextId: 34, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 33, + }, + }, + { + parentId: 33, + nextId: null, + node: { type: 3, textContent: '2', id: 36 }, + }, + { + parentId: 17, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 34, + }, + }, + { + parentId: 34, + nextId: null, + node: { type: 3, textContent: '1', id: 35 }, + }, + ], + }, + timestamp: now + 2000, + }, + // mutation that removes five div elements at 3000ms + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [ + { parentId: 17, id: 29 }, + { parentId: 17, id: 31 }, + { parentId: 17, id: 32 }, + { parentId: 17, id: 33 }, + { parentId: 17, id: 34 }, + ], + adds: [], + }, + timestamp: now + 3000, + }, +]; diff --git a/packages/rrweb/test/replay/__snapshots__/sync-replayer.test.ts.snap b/packages/rrweb/test/replay/__snapshots__/sync-replayer.test.ts.snap new file mode 100644 index 0000000000..4d6b2f189a --- /dev/null +++ b/packages/rrweb/test/replay/__snapshots__/sync-replayer.test.ts.snap @@ -0,0 +1,78 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`A synchronous replayer purely built with RRDom should play mutation events synchronously: Full Snapshot @ 100 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 18 RRText text=\\"\\\\nempty\\\\n\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 1000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 34 DIV + 35 RRText text=\\"1\\" + 33 DIV + 36 RRText text=\\"2\\" + 32 DIV + 37 RRText text=\\"3\\" + 31 DIV + 38 RRText text=\\"4\\" + 29 DIV + 30 RRText text=\\"5\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 2000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 29 DIV + 30 RRText text=\\"5\\" + 31 DIV + 38 RRText text=\\"4\\" + 32 DIV + 37 RRText text=\\"3\\" + 33 DIV + 36 RRText text=\\"2\\" + 34 DIV + 35 RRText text=\\"1\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`A synchronous replayer purely built with RRDom should play mutation events synchronously: Incremental Snapshot @ 3000 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; diff --git a/packages/rrweb/test/replay/sync-replayer.test.ts b/packages/rrweb/test/replay/sync-replayer.test.ts new file mode 100644 index 0000000000..900978557e --- /dev/null +++ b/packages/rrweb/test/replay/sync-replayer.test.ts @@ -0,0 +1,22 @@ +import { printRRDom } from 'rrdom'; +import { SyncReplayer } from '../../src/replay/sync-replayer'; +import { EventType, eventWithTime } from '../../src/types'; +import { events as mutationEvents } from '../events/node-mutation'; + +describe('A synchronous replayer purely built with RRDom', () => { + it('should play mutation events synchronously', () => { + const events = mutationEvents as eventWithTime[]; + const replayer = new SyncReplayer(events); + replayer.play(({ event, currentTime }) => { + if (event.type === EventType.FullSnapshot) { + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot(`Full Snapshot @ ${currentTime}`); + } else if (event.type === EventType.IncrementalSnapshot) { + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot(`Incremental Snapshot @ ${currentTime}`); + } + }); + }); +}); From 8530316bd5bbaca418ff65d0b9d28501bc744e33 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 11 Jul 2022 19:06:16 +1000 Subject: [PATCH 10/52] test: fix the broken unit test --- packages/rrweb-cutter/package.json | 6 ++--- packages/rrweb-cutter/test/html/main.html | 21 +++--------------- .../rrweb-cutter/test/session-cutter.test.ts | 20 +---------------- yarn.lock | 22 ------------------- 4 files changed, 7 insertions(+), 62 deletions(-) diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index 799fe471a2..ba0af3935d 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -34,8 +34,8 @@ "vite": "^2.9.13" }, "dependencies": { - "rrdom": "^0.1.2", - "rrweb-snapshot": "^1.1.14", - "rrweb": "^1.1.3" + "rrdom": "^0.1.3", + "rrweb-snapshot": "^2.0.0-alpha.0", + "rrweb": "^2.0.0-alpha.0" } } diff --git a/packages/rrweb-cutter/test/html/main.html b/packages/rrweb-cutter/test/html/main.html index 40e3c169e2..b7e6e9bab1 100644 --- a/packages/rrweb-cutter/test/html/main.html +++ b/packages/rrweb-cutter/test/html/main.html @@ -4,23 +4,8 @@ Main - - + +

This is a h1 heading

@@ -34,7 +19,7 @@

This is a h1 heading with styles

Text 2 - This is an image + This is an image
diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index de08d6cfbe..f3bc7df1de 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -68,14 +68,13 @@ describe('session cutter', () => { const mirror = createMirror(); // the full snapshot that is built on jsdom const originalSnapshot = snapshot(document, { mirror }); - if (originalSnapshot) snapshotFilter(originalSnapshot); + // Create a RRDom according to the jsdom (real dom). buildFromDom(document, mirror, rrdom); const newFullSnapshot = RRDomSnapshot(rrdom, { mirror: rrdom.mirror, }); - if (newFullSnapshot) snapshotFilter(newFullSnapshot); expect(newFullSnapshot).toEqual(originalSnapshot); }); }); @@ -155,20 +154,3 @@ function getHtml(fileName: string) { const filePath = path.resolve(__dirname, `./html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); } - -/** - * Some properties in the snapshot shouldn't be checked. - * 1. css styles' format are different. - * 2. href and src attributes are different. - */ -function snapshotFilter(n: serializedNodeWithId) { - if (n.type === NodeType.Element) { - delete n.attributes['href']; - delete n.attributes['src']; - } else if (n.type === NodeType.Text && n.isStyle) n.textContent = ''; - if ( - [NodeType.Document, NodeType.Element].includes(n.type) && - (n as documentNode | elementNode).childNodes - ) - for (const child of (n as elementNode).childNodes) snapshotFilter(child); -} diff --git a/yarn.lock b/yarn.lock index 3bfb3e5400..d9f0a1f5f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8071,11 +8071,6 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -mitt@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" - integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== - mitt@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" @@ -10018,23 +10013,6 @@ rollup@^2.71.1: optionalDependencies: fsevents "~2.3.2" -rrweb-snapshot@^1.1.14: - version "1.1.14" - resolved "https://registry.yarnpkg.com/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz#9d4d9be54a28a893373428ee4393ec7e5bd83fcc" - integrity sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ== - -rrweb@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/rrweb/-/rrweb-1.1.3.tgz#4fbb3d473d71c79b6c30a54e585e5a01c8ac08bb" - integrity sha512-F2qp8LteJLyycsv+lCVJqtVpery63L3U+/ogqMA0da8R7Jx57o6gT+HpjrzdeeGMIBZR7kKNaKyJwDupTTu5KA== - dependencies: - "@types/css-font-loading-module" "0.0.7" - "@xstate/fsm" "^1.4.0" - base64-arraybuffer "^1.0.1" - fflate "^0.4.4" - mitt "^1.1.3" - rrweb-snapshot "^1.1.14" - run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" From cafe5948494ef51b41114bc4a49a49a5f9b43b62 Mon Sep 17 00:00:00 2001 From: Juice10 Date: Tue, 27 Sep 2022 20:48:15 +0000 Subject: [PATCH 11/52] Apply formatting changes --- .../rrweb/typings/replay/sync-replayer.d.ts | 89 +++++++++++-------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/packages/rrweb/typings/replay/sync-replayer.d.ts b/packages/rrweb/typings/replay/sync-replayer.d.ts index f2083ec035..8b492c5386 100644 --- a/packages/rrweb/typings/replay/sync-replayer.d.ts +++ b/packages/rrweb/typings/replay/sync-replayer.d.ts @@ -1,46 +1,57 @@ import { RRDocument as BaseRRDocument } from 'rrdom'; import type { Mirror as RRDOMMirror } from 'rrdom'; -import { eventWithTime, playerConfig, playerMetaData, Handler, mouseMovePos } from '../types'; +import { + eventWithTime, + playerConfig, + playerMetaData, + Handler, + mouseMovePos, +} from '../types'; declare class RRDocument extends BaseRRDocument { - scrollTop: number; - scrollLeft: number; + scrollTop: number; + scrollLeft: number; } export declare class SyncReplayer { - config: Partial; - virtualDom: RRDocument; - mousePos: mouseMovePos | null; - events: eventWithTime[]; - latestMetaEvent: eventWithTime | null; - unhandledEvents: eventWithTime[]; - private currentTime; - private emitter; - private legacy_missingNodeRetryMap; - private cache; - private mirror; - private newDocumentQueue; - constructor(events: Array, config?: Partial); - on(event: string, handler: Handler): this; - off(event: string, handler: Handler): this; - setConfig(config: Partial): void; - getMetaData(): playerMetaData; - getCurrentTime(): number; - getMirror(): RRDOMMirror; - play(castEventCallback?: (event: { - index: number; - event: eventWithTime; - currentTime: number; - }) => boolean | void): void; - resetCache(): void; - private getCastFn; - private rebuildFullSnapshot; - private attachDocumentToIframe; - private collectIframeAndAttachDocument; - private applyIncremental; - private applyMutation; - private legacy_resolveMissingNode; - private warnNodeNotFound; - private debugNodeNotFound; - private warn; - private debug; + config: Partial; + virtualDom: RRDocument; + mousePos: mouseMovePos | null; + events: eventWithTime[]; + latestMetaEvent: eventWithTime | null; + unhandledEvents: eventWithTime[]; + private currentTime; + private emitter; + private legacy_missingNodeRetryMap; + private cache; + private mirror; + private newDocumentQueue; + constructor( + events: Array, + config?: Partial, + ); + on(event: string, handler: Handler): this; + off(event: string, handler: Handler): this; + setConfig(config: Partial): void; + getMetaData(): playerMetaData; + getCurrentTime(): number; + getMirror(): RRDOMMirror; + play( + castEventCallback?: (event: { + index: number; + event: eventWithTime; + currentTime: number; + }) => boolean | void, + ): void; + resetCache(): void; + private getCastFn; + private rebuildFullSnapshot; + private attachDocumentToIframe; + private collectIframeAndAttachDocument; + private applyIncremental; + private applyMutation; + private legacy_resolveMissingNode; + private warnNodeNotFound; + private debugNodeNotFound; + private warn; + private debug; } export {}; From 5f442b65a0b15c9d7310eb179d06bce6a64cef4d Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Wed, 28 Sep 2022 00:17:36 +0200 Subject: [PATCH 12/52] add pruneBranches --- packages/rrweb-cutter/src/index.ts | 130 +++++++++++++++++- .../__snapshots__/session-cutter.test.ts.snap | 47 +++++++ .../rrweb-cutter/test/session-cutter.test.ts | 74 +++++++++- 3 files changed, 249 insertions(+), 2 deletions(-) diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index ed6c45d4ae..531de72796 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,6 +1,8 @@ -import type { eventWithTime } from 'rrweb/typings/types'; +import type { eventWithTime, mousePosition } from 'rrweb/typings/types'; +import { IncrementalSource } from 'rrweb'; import { EventType, SyncReplayer } from 'rrweb'; import snapshot from './snapshot'; +import { serializedNodeWithId } from 'rrweb-snapshot'; type CutterConfig = { points: number[]; }; @@ -96,6 +98,132 @@ function cutEvents( return result; } +export function pruneBranches( + events: eventWithTime[], + { keep }: { keep: number[] }, +): eventWithTime[] { + const result: eventWithTime[] = []; + const replayer = new SyncReplayer(events); + const treeSet = new Set(); + replayer.play(({ event }) => { + if ( + [ + EventType.Meta, + EventType.Load, + EventType.DomContentLoaded, + EventType.Custom, + EventType.Plugin, + ].includes(event.type) + ) { + result.push(event); + } else if (event.type === EventType.FullSnapshot) { + const { node } = event.data; + keep.forEach((id) => { + const tree = getTreeForId(id, node); + tree.forEach((id) => treeSet.add(id)); + }); + const prunedNode = reconstructTreeWithIds(node, treeSet); + if (prunedNode) + result.push({ + ...event, + data: { + ...event.data, + node: prunedNode, + }, + } as eventWithTime); + } else if (event.type === EventType.IncrementalSnapshot) { + if ('positions' in event.data) { + const { positions } = event.data; + const prunedPositions: mousePosition[] = positions.filter((p) => + treeSet.has(p.id), + ); + if (prunedPositions.length > 0) + result.push({ + ...event, + data: { + ...event.data, + positions: prunedPositions, + }, + } as eventWithTime); + } else if ('id' in event.data) { + if (treeSet.has(event.data.id)) result.push(event); + } else if (event.data.source === IncrementalSource.Mutation) { + const { removes, adds, texts, attributes } = event.data; + const prunedRemoves = removes.filter((remove) => + treeSet.has(remove.id), + ); + const prunedAdds = adds.filter((add) => treeSet.has(add.parentId)); + const prunedTexts = texts.filter((text) => treeSet.has(text.id)); + const prunedAttributes = attributes.filter((attr) => + treeSet.has(attr.id), + ); + if ( + prunedRemoves.length > 0 || + prunedAdds.length > 0 || + prunedTexts.length > 0 || + prunedAttributes.length > 0 + ) + result.push({ + ...event, + data: { + ...event.data, + removes: prunedRemoves, + adds: prunedAdds, + texts: prunedTexts, + attributes: prunedAttributes, + }, + } as eventWithTime); + } + } + return true; + }); + return result; +} + +export function getTreeForId(id: number, node: serializedNodeWithId): number[] { + const results: number[] = []; + if (node.id === id) { + results.push(...getIdsInNode(node)); + } else if ('childNodes' in node) { + for (let i = 0; i < node.childNodes.length; i++) { + const child = node.childNodes[i]; + const childTree = getTreeForId(id, child); + if (childTree.length > 0) { + results.push(node.id, ...childTree); + break; + } + } + } + return results; +} + +export function getIdsInNode(node: serializedNodeWithId): Array { + const results: number[] = []; + results.push(node.id); + if ('childNodes' in node) { + for (let i = 0; i < node.childNodes.length; i++) { + const child = node.childNodes[i]; + results.push(...getIdsInNode(child)); + } + } + return results; +} + +export function reconstructTreeWithIds( + node: serializedNodeWithId, + ids: Set, +): serializedNodeWithId | undefined { + if (ids.has(node.id)) { + if ('childNodes' in node) { + node.childNodes = node.childNodes + .map((child) => reconstructTreeWithIds(child, ids)) + .filter(Boolean) as serializedNodeWithId[]; + } + return node; + } + return undefined; +} + export function getValidSortedPoints(points: number[], totalTime: number) { const validSortedPoints = []; for (let i = 0; i < points.length; i++) { diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index 893d7b99d3..87739452b3 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -1,4 +1,51 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`pruneBranches should cut branches that doesn't include id: pruned all but 14 1`] = ` +"1 RRDocument + -1 RRDocumentType + 2 HTML + 12 BODY + 14 DIV id=\\"root\\" + 15 RRText text=\\" \\\\n \\" +" +`; + +exports[`pruneBranches should remove mutations that don't include ids 1`] = ` +Object { + "data": Object { + "adds": Array [ + Object { + "nextId": null, + "node": Object {}, + "parentId": 14, + }, + ], + "attributes": Array [ + Object { + "attributes": Object { + "data-attr": "Kept", + }, + "id": 14, + }, + ], + "removes": Array [ + Object { + "id": 15, + "parentId": 14, + }, + ], + "source": 0, + "texts": Array [ + Object { + "id": 15, + "value": "Kept", + }, + ], + }, + "type": 3, +} +`; + exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 1000ms 1`] = ` "1 RRDocument 2 HTML diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index f3bc7df1de..1271cb6509 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -14,7 +14,7 @@ import { import { EventType, SyncReplayer } from 'rrweb'; import type { eventWithTime } from 'rrweb/typings/types'; import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; -import { sessionCut, getValidSortedPoints } from '../src'; +import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { events as inlineStyleEvents } from './events/inline-style.event'; @@ -150,6 +150,78 @@ describe('session cutter', () => { }); }); +describe('pruneBranches', () => { + it("should cut branches that doesn't include id", () => { + const events = inlineStyleEvents as eventWithTime[]; + const result = pruneBranches(events, { keep: [14] }); + expect(result).toHaveLength(5); + const replayer = new SyncReplayer(result); + replayer.play(); + + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('pruned all but 14'); + }); + + it("should remove mutations that don't include ids", () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [ + { + id: 15, + value: 'Kept', + }, + { + id: 1001, + value: 'Cut', + }, + ], + attributes: [ + { + id: 14, + attributes: { + 'data-attr': 'Kept', + }, + }, + { + id: 1002, + attributes: { + 'data-attr': 'Cut', + }, + }, + ], + removes: [ + { + parentId: 14, + id: 15, + }, + { + parentId: 1002, + id: 1003, + }, + ], + adds: [ + { + parentId: 14, + nextId: null, + node: {}, + }, + { + parentId: 1001, + nextId: null, + node: {}, + }, + ], + }, + }; + const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [14] }); + expect(result[result.length - 1]).toMatchSnapshot(); + }); +}); + function getHtml(fileName: string) { const filePath = path.resolve(__dirname, `./html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); From 247edce72177c1edbe0b989e82f0dff3e11fdfd8 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 29 Sep 2022 00:33:07 +0200 Subject: [PATCH 13/52] use mutations to construct the pruneBranches tree as well --- packages/rrweb-cutter/src/index.ts | 87 +++++++-- .../__snapshots__/session-cutter.test.ts.snap | 174 ++++++++++++++++++ .../rrweb-cutter/test/session-cutter.test.ts | 65 +++++++ packages/rrweb-cutter/test/utils.ts | 15 ++ packages/rrweb/src/replay/sync-replayer.ts | 24 +++ .../rrweb/typings/replay/sync-replayer.d.ts | 57 ------ 6 files changed, 345 insertions(+), 77 deletions(-) create mode 100644 packages/rrweb-cutter/test/utils.ts delete mode 100644 packages/rrweb/typings/replay/sync-replayer.d.ts diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index 531de72796..2c7b6dd16d 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,4 +1,8 @@ -import type { eventWithTime, mousePosition } from 'rrweb/typings/types'; +import type { + addedNodeMutation, + eventWithTime, + mousePosition, +} from 'rrweb/typings/types'; import { IncrementalSource } from 'rrweb'; import { EventType, SyncReplayer } from 'rrweb'; import snapshot from './snapshot'; @@ -104,24 +108,51 @@ export function pruneBranches( ): eventWithTime[] { const result: eventWithTime[] = []; const replayer = new SyncReplayer(events); - const treeSet = new Set(); + const treeSet = new Set(keep); + replayer.reversePlay(({ event }) => { + if (event.type === EventType.FullSnapshot) { + const { node } = event.data; + treeSet.forEach((id) => { + const tree = getTreeForId(id, node, keep.includes(id)); + tree.forEach((id) => treeSet.add(id)); + }); + } else if (event.type === EventType.IncrementalSnapshot) { + if (event.data.source === IncrementalSource.Mutation) { + const { adds } = event.data; + adds.forEach((add) => { + if (treeSet.has(add.node.id)) { + const tree = getTreeForId( + add.node.id, + add.node, + keep.includes(add.node.id), + ); + treeSet.add(add.parentId); + tree.forEach((id) => treeSet.add(id)); + } else if ( + 'childNodes' in add.node && + add.node.childNodes.length > 0 + ) { + treeSet.forEach((id) => { + const tree = getTreeForId(id, add.node, keep.includes(id)); + if (tree.length) treeSet.add(add.parentId); + tree.forEach((id) => treeSet.add(id)); + }); + } + }); + } + } + return true; + }); + replayer.play(({ event }) => { if ( - [ - EventType.Meta, - EventType.Load, - EventType.DomContentLoaded, - EventType.Custom, - EventType.Plugin, - ].includes(event.type) + [EventType.Meta, EventType.Load, EventType.DomContentLoaded].includes( + event.type, + ) ) { result.push(event); } else if (event.type === EventType.FullSnapshot) { const { node } = event.data; - keep.forEach((id) => { - const tree = getTreeForId(id, node); - tree.forEach((id) => treeSet.add(id)); - }); const prunedNode = reconstructTreeWithIds(node, treeSet); if (prunedNode) result.push({ @@ -152,7 +183,16 @@ export function pruneBranches( const prunedRemoves = removes.filter((remove) => treeSet.has(remove.id), ); - const prunedAdds = adds.filter((add) => treeSet.has(add.parentId)); + const prunedAdds = adds + .map((add) => + treeSet.has(add.parentId) && keep.includes(add.parentId) + ? add + : { + ...add, + node: reconstructTreeWithIds(add.node, treeSet), + }, + ) + .filter((add) => Boolean(add.node)) as addedNodeMutation[]; const prunedTexts = texts.filter((text) => treeSet.has(text.id)); const prunedAttributes = attributes.filter((attr) => treeSet.has(attr.id), @@ -180,14 +220,18 @@ export function pruneBranches( return result; } -export function getTreeForId(id: number, node: serializedNodeWithId): number[] { +export function getTreeForId( + id: number, + node: serializedNodeWithId, + includeChildren: boolean, +): number[] { const results: number[] = []; if (node.id === id) { - results.push(...getIdsInNode(node)); + results.push(...getIdsInNode(node, includeChildren)); } else if ('childNodes' in node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; - const childTree = getTreeForId(id, child); + const childTree = getTreeForId(id, child, includeChildren); if (childTree.length > 0) { results.push(node.id, ...childTree); break; @@ -197,13 +241,16 @@ export function getTreeForId(id: number, node: serializedNodeWithId): number[] { return results; } -export function getIdsInNode(node: serializedNodeWithId): Array { +export function getIdsInNode( + node: serializedNodeWithId, + includeChildren: boolean, +): Array { const results: number[] = []; results.push(node.id); - if ('childNodes' in node) { + if (includeChildren && 'childNodes' in node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; - results.push(...getIdsInNode(child)); + results.push(...getIdsInNode(child, includeChildren)); } } return results; diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index 87739452b3..bffc2fbd93 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -10,6 +10,180 @@ exports[`pruneBranches should cut branches that doesn't include id: pruned all b " `; +exports[`pruneBranches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" + }, + \\"childNodes\\": [], + \\"id\\": 14 + } + ], + \\"id\\": 12 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"id\\": 98, + \\"type\\": 2, + \\"tagName\\": \\"main\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"id\\": 99, + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [] + } + ] + } + } + ] + } + } +]" +`; + +exports[`pruneBranches should remove branches based on nodes that came in after fullsnapshot 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" + }, + \\"childNodes\\": [], + \\"id\\": 14 + } + ], + \\"id\\": 12 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"id\\": 99, + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [] + } + } + ] + } + } +]" +`; + exports[`pruneBranches should remove mutations that don't include ids 1`] = ` Object { "data": Object { diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index 1271cb6509..6c6a7e2bbb 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -18,6 +18,7 @@ import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { events as inlineStyleEvents } from './events/inline-style.event'; +import { assertSnapshot } from './utils'; describe('session cutter', () => { it('should return the same events if the events length is too short', () => { @@ -220,6 +221,70 @@ describe('pruneBranches', () => { const result = pruneBranches(events, { keep: [14] }); expect(result[result.length - 1]).toMatchSnapshot(); }); + + it('should remove branches based on nodes that came in after fullsnapshot', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 14, + nextId: null, + node: { + id: 99, + type: NodeType.Element, + tagName: 'canvas', + attributes: {}, + childNodes: [], + }, + }, + ], + }, + }; + const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [99] }); + assertSnapshot(result); + }); + it('should remove branches based on child nodes that came in after fullsnapshot', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 14, + nextId: null, + node: { + id: 98, + type: NodeType.Element, + tagName: 'main', + attributes: {}, + childNodes: [ + { + id: 99, + type: NodeType.Element, + tagName: 'canvas', + attributes: {}, + childNodes: [], + }, + ], + }, + }, + ], + }, + }; + const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [99] }); + assertSnapshot(result); + }); + }); function getHtml(fileName: string) { diff --git a/packages/rrweb-cutter/test/utils.ts b/packages/rrweb-cutter/test/utils.ts new file mode 100644 index 0000000000..a9a2d3568a --- /dev/null +++ b/packages/rrweb-cutter/test/utils.ts @@ -0,0 +1,15 @@ +import { assertSnapshot as originalAssertSnapshot } from 'rrweb/test/utils'; +import { eventWithTime } from 'rrweb/typings/types'; +export function assertSnapshot(events: eventWithTime[]) { + /** + * for some reason there seems to be an inconsistency between + * import("rrweb/packages/rrweb/typings/types") + * and import("rrweb/packages/rrweb/src/types") + * which spawns the following error: + * Type 'incrementalSnapshotEvent & { timestamp: number; delay?: number | undefined; }' is not assignable to type 'incrementalSnapshotEvent'. + * @see https://ts-error-translator.vercel.app/?error=IIJw5grgtgpgdgFwAQHsBmSEE8AOMkDkAllDiiAgBQBEA9AKoDOMIjtAVhIwkXLSDDJsQIAO4wARrRwBDAMYBrGWBjCxk2thy8wbLauoBKAHQwAbvAQB1IggAWAFRIwA2gF0CSIoyRwUyGUZGIjA4GQkAG3wEFCRZEBlYBBZUDH1CEjIKGgZmVg4uHj4BIX51KVlFZVUy8SlGEDlNXAMTc0sbeydYdwJjACgkJAcWjNJyKjomFjZObl5+QRQ1Oul5JRUVjS0dPRbGI1MLRE7HZ09vX38kQODQ8KjMWPTiceypvNnChZLl2o1KhsaiJVg0mvoDm1jtZbGdYH1BkNhqNiHA5AJYIgZBEAMphHCMOz+ACi0KQADIkABvTDObiJHAALl80AkLAA3EgACYwCIyLAAfmZcFZKQAPkgIHAeWheDAuZyAL4XHx+AJBEJhSLRZ4o9onWHdGAIpFIkZ4MZZSa5GYFebFJZbCrrapO5raOC6d2tYy8dEwTEIbF4mQEokIUmWCnU2mwemkYWikCcnl8wWJqBskBICVSmVyhVIZVeVXXW6ah46zAozITHLTfJzIqLUoggEuzb-eqNb2Q31ojGWYP4wkksmUmk8ONBhMszMc7m8-lCudZnOS6UwWVweVKk2ms01gcBoe4kfhyOIaOTukzpmrhep5cZte5zfb3dFlVXdV3LWPGJqwtVF-UDYdQ1HCNoX3A9DzwHx0DiEAUDwCgsEILkZCDTwZAELw0RQUgsKIbUBlg2DzXwV4rXrT47WbX43UBV0u3dXZe0OP1ByxCIABEsJkb81RuDV7m1J4gKo2t3htRtvgdVtyjWKpOzbbtwX2TjjzAviBJg8jTUowhYC5IgZAASUQFh5B4FA4H47CSx-ES-0rCSXi4k8eIcwSyIM8ijOousPltJsfkdVjmNUpSdk9PZ4MOEyzMs5IEjkWz7L0pzhPLMSAN1YDpOtBsvntFs-jU5SgTdMEOJMJKLKstKMp8-T-IPQKGpS6z0qIOzWuystRP-KsXi6pqbL6uAAAVcMSNr2o6-ZUiQlCWGwQh9BwvC-UI2QeFIxFFv8wKito0L5PKpiO2BGLcHYiFDgAWXlZKJt6uzGCEobXPEwCPLeYq6LChSKqUqLbtBHtHpMF7TMa1LJs+vogA + * disabling the type check for now + */ + // @ts-ignore + originalAssertSnapshot(events); +} diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index bdef239461..76517ac457 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -197,6 +197,30 @@ export class SyncReplayer { } } + public reversePlay( + castEventCallback?: (event: { + index: number; + event: eventWithTime; + currentTime: number; + }) => boolean | void, + ) { + const baseTime = this.events[0].timestamp; + for (let i = this.events.length - 1; i >= 0; i--) { + const event = this.events[i]; + const castFn = this.getCastFn(event); + castFn(); + this.currentTime = event.timestamp - baseTime; + if ( + castEventCallback?.({ + index: i, + event: event, + currentTime: this.currentTime, + }) === false + ) + break; + } + } + /** * Empties the replayer's cache and reclaims memory. * The replayer will use this cache to speed up the playback. diff --git a/packages/rrweb/typings/replay/sync-replayer.d.ts b/packages/rrweb/typings/replay/sync-replayer.d.ts deleted file mode 100644 index 8b492c5386..0000000000 --- a/packages/rrweb/typings/replay/sync-replayer.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { RRDocument as BaseRRDocument } from 'rrdom'; -import type { Mirror as RRDOMMirror } from 'rrdom'; -import { - eventWithTime, - playerConfig, - playerMetaData, - Handler, - mouseMovePos, -} from '../types'; -declare class RRDocument extends BaseRRDocument { - scrollTop: number; - scrollLeft: number; -} -export declare class SyncReplayer { - config: Partial; - virtualDom: RRDocument; - mousePos: mouseMovePos | null; - events: eventWithTime[]; - latestMetaEvent: eventWithTime | null; - unhandledEvents: eventWithTime[]; - private currentTime; - private emitter; - private legacy_missingNodeRetryMap; - private cache; - private mirror; - private newDocumentQueue; - constructor( - events: Array, - config?: Partial, - ); - on(event: string, handler: Handler): this; - off(event: string, handler: Handler): this; - setConfig(config: Partial): void; - getMetaData(): playerMetaData; - getCurrentTime(): number; - getMirror(): RRDOMMirror; - play( - castEventCallback?: (event: { - index: number; - event: eventWithTime; - currentTime: number; - }) => boolean | void, - ): void; - resetCache(): void; - private getCastFn; - private rebuildFullSnapshot; - private attachDocumentToIframe; - private collectIframeAndAttachDocument; - private applyIncremental; - private applyMutation; - private legacy_resolveMissingNode; - private warnNodeNotFound; - private debugNodeNotFound; - private warn; - private debug; -} -export {}; From b29b5df6c8ad9dae33b38fba6cd6f4232775dcd6 Mon Sep 17 00:00:00 2001 From: Juice10 Date: Wed, 28 Sep 2022 22:35:47 +0000 Subject: [PATCH 14/52] Apply formatting changes --- packages/rrweb-cutter/test/session-cutter.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index 6c6a7e2bbb..32359d2234 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -284,7 +284,6 @@ describe('pruneBranches', () => { const result = pruneBranches(events, { keep: [99] }); assertSnapshot(result); }); - }); function getHtml(fileName: string) { From ecc633294483e8f59ffb3ef8e1fefec05292e3c5 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 10 Nov 2022 12:23:46 +0100 Subject: [PATCH 15/52] Isolate tests by making sure inlineStyleEvents is unique each time --- .../rrweb-cutter/test/events/inline-style.event.ts | 2 +- packages/rrweb-cutter/test/session-cutter.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/rrweb-cutter/test/events/inline-style.event.ts b/packages/rrweb-cutter/test/events/inline-style.event.ts index b5095bb346..286d2d472c 100644 --- a/packages/rrweb-cutter/test/events/inline-style.event.ts +++ b/packages/rrweb-cutter/test/events/inline-style.event.ts @@ -2,7 +2,7 @@ import { EventType, eventWithTime, IncrementalSource } from 'rrweb/src/types'; const now = Date.now(); -export const events: eventWithTime[] = [ +export const eventsFn = (): eventWithTime[] => [ { type: EventType.DomContentLoaded, data: {}, diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index 32359d2234..7a9edd472c 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -17,7 +17,7 @@ import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; -import { events as inlineStyleEvents } from './events/inline-style.event'; +import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; import { assertSnapshot } from './utils'; describe('session cutter', () => { @@ -129,7 +129,7 @@ describe('session cutter', () => { }); it('should cut events with inline styles', () => { - const events = inlineStyleEvents as eventWithTime[]; + const events = inlineStyleEvents() as eventWithTime[]; const result = sessionCut(events, { points: [1000] }); expect(result).toHaveLength(2); // all events before 1000ms @@ -153,7 +153,7 @@ describe('session cutter', () => { describe('pruneBranches', () => { it("should cut branches that doesn't include id", () => { - const events = inlineStyleEvents as eventWithTime[]; + const events = inlineStyleEvents() as eventWithTime[]; const result = pruneBranches(events, { keep: [14] }); expect(result).toHaveLength(5); const replayer = new SyncReplayer(result); @@ -217,7 +217,7 @@ describe('pruneBranches', () => { ], }, }; - const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; const result = pruneBranches(events, { keep: [14] }); expect(result[result.length - 1]).toMatchSnapshot(); }); @@ -245,7 +245,7 @@ describe('pruneBranches', () => { ], }, }; - const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; const result = pruneBranches(events, { keep: [99] }); assertSnapshot(result); }); @@ -280,7 +280,7 @@ describe('pruneBranches', () => { ], }, }; - const events = [...inlineStyleEvents, mutationEvent] as eventWithTime[]; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; const result = pruneBranches(events, { keep: [99] }); assertSnapshot(result); }); From b981c5c7965eba3630c56d18cbc49ff1174d563f Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 10 Nov 2022 12:24:58 +0100 Subject: [PATCH 16/52] Decrease the number of iterations needed Based on https://github.com/rrweb-io/rrweb/pull/1011#discussion_r982445239 --- packages/rrweb-cutter/src/index.ts | 38 ++++++++++++------------------ 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index 2c7b6dd16d..bc380e924c 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -112,31 +112,23 @@ export function pruneBranches( replayer.reversePlay(({ event }) => { if (event.type === EventType.FullSnapshot) { const { node } = event.data; - treeSet.forEach((id) => { - const tree = getTreeForId(id, node, keep.includes(id)); - tree.forEach((id) => treeSet.add(id)); - }); + const tree = getTreeForId(treeSet, node, keep); + tree.forEach((id) => treeSet.add(id)); } else if (event.type === EventType.IncrementalSnapshot) { if (event.data.source === IncrementalSource.Mutation) { const { adds } = event.data; adds.forEach((add) => { - if (treeSet.has(add.node.id)) { - const tree = getTreeForId( - add.node.id, - add.node, - keep.includes(add.node.id), - ); + const tree = getTreeForId(treeSet, add.node, keep); + if (tree.length) { treeSet.add(add.parentId); tree.forEach((id) => treeSet.add(id)); } else if ( 'childNodes' in add.node && add.node.childNodes.length > 0 ) { - treeSet.forEach((id) => { - const tree = getTreeForId(id, add.node, keep.includes(id)); - if (tree.length) treeSet.add(add.parentId); - tree.forEach((id) => treeSet.add(id)); - }); + const tree = getTreeForId(treeSet, add.node, keep); + if (tree.length) treeSet.add(add.parentId); + tree.forEach((id) => treeSet.add(id)); } }); } @@ -221,17 +213,17 @@ export function pruneBranches( } export function getTreeForId( - id: number, + treeSet: Set, node: serializedNodeWithId, - includeChildren: boolean, + keepIds: number[], ): number[] { const results: number[] = []; - if (node.id === id) { - results.push(...getIdsInNode(node, includeChildren)); + if (treeSet.has(node.id)) { + results.push(...getIdsInNode(node, keepIds)); } else if ('childNodes' in node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; - const childTree = getTreeForId(id, child, includeChildren); + const childTree = getTreeForId(treeSet, child, keepIds); if (childTree.length > 0) { results.push(node.id, ...childTree); break; @@ -243,14 +235,14 @@ export function getTreeForId( export function getIdsInNode( node: serializedNodeWithId, - includeChildren: boolean, + keepIds: number[], ): Array { const results: number[] = []; results.push(node.id); - if (includeChildren && 'childNodes' in node) { + if (keepIds.includes(node.id) && 'childNodes' in node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; - results.push(...getIdsInNode(child, includeChildren)); + results.push(...getIdsInNode(child, keepIds)); } } return results; From 5d3c9059e23e248e4aa5b358ac8dd13c35f3c79f Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 10 Nov 2022 14:43:57 +0100 Subject: [PATCH 17/52] Prune now also support moved nodes --- packages/rrweb-cutter/src/index.ts | 21 ++-- .../__snapshots__/session-cutter.test.ts.snap | 117 ++++++++++++++++++ .../rrweb-cutter/test/session-cutter.test.ts | 27 ++++ packages/rrweb-cutter/test/unit.test.ts | 106 ++++++++++++++++ 4 files changed, 262 insertions(+), 9 deletions(-) create mode 100644 packages/rrweb-cutter/test/unit.test.ts diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index bc380e924c..f1346d5a36 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -116,10 +116,13 @@ export function pruneBranches( tree.forEach((id) => treeSet.add(id)); } else if (event.type === EventType.IncrementalSnapshot) { if (event.data.source === IncrementalSource.Mutation) { - const { adds } = event.data; + const { adds, removes } = event.data; + removes.forEach((remove) => { + if (treeSet.has(remove.id)) treeSet.add(remove.parentId); + }); adds.forEach((add) => { const tree = getTreeForId(treeSet, add.node, keep); - if (tree.length) { + if (tree.size) { treeSet.add(add.parentId); tree.forEach((id) => treeSet.add(id)); } else if ( @@ -127,7 +130,7 @@ export function pruneBranches( add.node.childNodes.length > 0 ) { const tree = getTreeForId(treeSet, add.node, keep); - if (tree.length) treeSet.add(add.parentId); + if (tree.size) treeSet.add(add.parentId); tree.forEach((id) => treeSet.add(id)); } }); @@ -216,17 +219,17 @@ export function getTreeForId( treeSet: Set, node: serializedNodeWithId, keepIds: number[], -): number[] { - const results: number[] = []; +): Set { + const results = new Set(); if (treeSet.has(node.id)) { - results.push(...getIdsInNode(node, keepIds)); + getIdsInNode(node, keepIds).forEach((id) => results.add(id)); } else if ('childNodes' in node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; const childTree = getTreeForId(treeSet, child, keepIds); - if (childTree.length > 0) { - results.push(node.id, ...childTree); - break; + if (childTree.size > 0) { + results.add(node.id); + childTree.forEach((id) => results.add(id)); } } } diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index bffc2fbd93..d113052141 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -10,6 +10,123 @@ exports[`pruneBranches should cut branches that doesn't include id: pruned all b " `; +exports[`pruneBranches should keep branches where target child nodes was, and gets moved to 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"utf-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 5 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\" \\\\n \\", + \\"id\\": 15 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 12 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 15 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 14, + \\"id\\": 15 + } + ], + \\"adds\\": [ + { + \\"parentId\\": 5, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\" \\\\n \\", + \\"id\\": 15 + } + } + ] + } + } +]" +`; + exports[`pruneBranches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` "[ { diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index 7a9edd472c..d2fa3617f8 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -284,6 +284,33 @@ describe('pruneBranches', () => { const result = pruneBranches(events, { keep: [99] }); assertSnapshot(result); }); + + it('should keep branches where target child nodes was, and gets moved to', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [ + { + parentId: 14, + id: 15, + }, + ], + adds: [ + { + parentId: 5, + nextId: null, + node: { type: 3, textContent: ' \n ', id: 15 }, + }, + ], + }, + }; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [15] }); + assertSnapshot(result); + }); }); function getHtml(fileName: string) { diff --git a/packages/rrweb-cutter/test/unit.test.ts b/packages/rrweb-cutter/test/unit.test.ts new file mode 100644 index 0000000000..3f0dad3f8c --- /dev/null +++ b/packages/rrweb-cutter/test/unit.test.ts @@ -0,0 +1,106 @@ +import { NodeType } from 'rrweb-snapshot'; +import { getTreeForId } from '../src'; + +describe('getTreeForId', () => { + it('should return parents id as part of tree', () => { + const tree = getTreeForId( + new Set([99]), + { + id: 1, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 2, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 99, + type: NodeType.Text, + textContent: 'hello world', + }, + ], + }, + ], + }, + [], + ); + console.log(tree); + expect(tree).toEqual(new Set([1, 2, 99])); + }); + it('should return parents id as part of tree 2', () => { + const tree = getTreeForId( + new Set([2]), + { + id: 1, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 2, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 99, + type: NodeType.Text, + textContent: 'hello world', + }, + ], + }, + ], + }, + [], + ); + console.log(tree); + expect(tree).toEqual(new Set([1, 2])); + }); + + it('should return multiple parent ids as part of tree', () => { + const tree = getTreeForId( + new Set([98, 99]), + { + id: 1, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 2, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 98, + type: NodeType.Text, + textContent: 'hello world', + }, + ], + }, + { + id: 3, + type: NodeType.Element, + tagName: 'div', + attributes: {}, + childNodes: [ + { + id: 99, + type: NodeType.Text, + textContent: 'hello world', + }, + ], + }, + ], + }, + [], + ); + console.log(tree); + expect(tree).toEqual(new Set([1, 2, 98, 3, 99])); + }); +}); From 10b6c6d393bb9aefff18355d0aa9a5df8225b4d2 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 10 Nov 2022 14:45:35 +0100 Subject: [PATCH 18/52] Add `yarn prune-events` script. Works as follows, if you'd like to prune all nodes in input.json apart from those related to id 100, run the following command: `yarn prune-events input.json output.json 100` --- packages/rrweb-cutter/package.json | 1 + packages/rrweb-cutter/scripts/prune.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 packages/rrweb-cutter/scripts/prune.ts diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index ba0af3935d..2ab0caa5b4 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -6,6 +6,7 @@ "dev": "vite build --watch", "bundle": "vite build && tsc --emitDeclarationOnly", "test": "jest", + "prune-events": "npx vite-node scripts/prune.ts --", "prepublish": "npm run bundle", "lint": "yarn eslint src/**/*.ts" }, 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 ( ...)'); + process.exit(1); +} + +const [input, output, ...ids] = myArgs; +const events = JSON.parse(fs.readFileSync(input, 'utf8')); +const result = pruneBranches(events, { keep: ids.map((id) => parseInt(id)) }); +fs.writeFileSync(output, JSON.stringify(result)); From 9605839e6fa58ab968c58b01d9954f56881395da Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 13 Feb 2023 17:20:29 +1100 Subject: [PATCH 19/52] update dependencies and restore changes to rrdom --- .eslintrc.js | 3 - .github/ISSUE_TEMPLATE/bug_report.yml | 5 +- .github/ISSUE_TEMPLATE/feature_request.yml | 5 +- packages/rrdom/src/index.ts | 11 +- packages/rrdom/test/virtual-dom.test.ts | 31 +- packages/rrdom/tsconfig.json | 6 +- packages/rrweb-cutter/.eslintignore | 2 +- packages/rrweb-cutter/package.json | 45 +- packages/rrweb-cutter/vite.config.ts | 26 +- packages/types/package.json | 6 +- yarn.lock | 452 +++++++-------------- 11 files changed, 239 insertions(+), 353 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 76b144807b..8dac0c2368 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,8 +21,5 @@ module.exports = { plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc', 'jest', 'compat'], rules: { 'tsdoc/syntax': 'warn', - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', }, }; diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1fff641682..477b504043 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 - Other (specify below) validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index df2a0d7f2a..2d402fb095 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 - Other (specify below) validations: required: true diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index a19413b4c1..eda0dd1912 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -141,7 +141,7 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) { } } -export class RRDocumentType extends BaseRRDocumentTypeImpl(RRNode) {} +export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); export class RRElement extends BaseRRElementImpl(RRNode) { inputData: inputData | null = null; @@ -176,11 +176,14 @@ export class RRIFrameElement extends RRElement { } } -export class RRText extends BaseRRTextImpl(RRNode) {} +export const RRText = BaseRRTextImpl(RRNode); +export type RRText = typeof RRText; -export class RRComment extends BaseRRCommentImpl(RRNode) {} +export const RRComment = BaseRRCommentImpl(RRNode); +export type RRComment = typeof RRComment; -export class RRCDATASection extends BaseRRCDATASectionImpl(RRNode) {} +export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); +export type RRCDATASection = typeof RRCDATASection; interface RRElementTagNameMap { audio: RRMediaElement; diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index 97201e0f70..b99aca5ae4 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -28,6 +28,27 @@ import { } from '../src'; import { compileTSCode } from './utils'; +const printRRDomCode = ` +/** + * Print the RRDom as a string. + * @param rootNode the root node of the RRDom tree + * @returns printed string + */ +function printRRDom(rootNode, mirror) { + return walk(rootNode, mirror, ''); +} +function walk(node, mirror, blankSpace) { + let printText = \`\${blankSpace}\${mirror.getId(node)} \${node.toString()}\n\`; + if(node instanceof rrdom.RRElement && node.shadowRoot) + printText += walk(node.shadowRoot, mirror, blankSpace + ' '); + for (const child of node.childNodes) + printText += walk(child, mirror, blankSpace + ' '); + if (node instanceof rrdom.RRIFrameElement) + printText += walk(node.contentDocument, mirror, blankSpace + ' '); + return printText; +} +`; + describe('RRDocument for browser environment', () => { jest.setTimeout(60_000); let mirror: Mirror; @@ -204,7 +225,7 @@ describe('RRDocument for browser environment', () => { beforeEach(async () => { page = await browser.newPage(); await page.goto('about:blank'); - await page.evaluate(code); + await page.evaluate(code + printRRDomCode); }); afterEach(async () => { @@ -215,7 +236,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - rrdom.printRRDom(doc, doc.mirror); + printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -225,7 +246,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - rrdom.printRRDom(doc, doc.mirror); + printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -235,7 +256,7 @@ describe('RRDocument for browser environment', () => { const result = await page.evaluate(` const doc = new rrdom.RRDocument(); rrdom.buildFromDom(document, undefined, doc); - rrdom.printRRDom(doc, doc.mirror); + printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); @@ -249,7 +270,7 @@ describe('RRDocument for browser environment', () => { const doc = new rrdom.RRDocument(); rrdom.buildFromDom(docu, undefined, doc); - rrdom.printRRDom(doc, doc.mirror); + printRRDom(doc, doc.mirror); `); expect(result).toMatchSnapshot(); }); diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 81ba5ca5e6..450e56e151 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "rootDir": "src", "composite": true, "target": "ES6", "module": "commonjs", @@ -9,13 +8,12 @@ "removeComments": true, "preserveConstEnums": true, "sourceMap": true, + "rootDir": "src", "outDir": "build", "lib": ["es6", "dom"], "skipLibCheck": true, "declaration": true, - "importsNotUsedAsValues": "error", - "esModuleInterop": true, - "strictBindCallApply": true + "importsNotUsedAsValues": "error" }, "references": [ { diff --git a/packages/rrweb-cutter/.eslintignore b/packages/rrweb-cutter/.eslintignore index 6c88efcd46..5a84bb05a1 100644 --- a/packages/rrweb-cutter/.eslintignore +++ b/packages/rrweb-cutter/.eslintignore @@ -1,3 +1,3 @@ vite.config.ts jest.config.js -test \ No newline at end of file +test diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index 2ab0caa5b4..04e6fbe005 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -1,14 +1,22 @@ { "name": "rrweb-cutter", - "version": "0.1.2", + "version": "0.1.7", "scripts": { - "check-types": "tsc -noEmit", - "dev": "vite build --watch", - "bundle": "vite build && tsc --emitDeclarationOnly", + "dev": "vite", "test": "jest", - "prune-events": "npx vite-node scripts/prune.ts --", - "prepublish": "npm run bundle", - "lint": "yarn eslint src/**/*.ts" + "build": "tsc -noEmit && vite build", + "check-types": "tsc -noEmit", + "prepublish": "npm run 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": [ @@ -17,26 +25,31 @@ "session cutter" ], "license": "MIT", - "main": "dist/index.umd.js", + "type": "module", + "main": "dist/index.umd.cjs", "unpkg": "dist/index.iife.js", "module": "dist/index.es.js", - "typings": "dist/types", + "typings": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.es.js", + "require": "./dist/index.umd.cjs" + } + }, "files": [ "dist" ], "devDependencies": { "@types/jest": "^27.4.1", - "@typescript-eslint/eslint-plugin": "^5.23.0", - "@typescript-eslint/parser": "^5.23.0", - "eslint": "^8.15.0", "jest": "^27.5.1", "ts-jest": "^27.1.3", "typescript": "^4.7.3", - "vite": "^2.9.13" + "vite": "^4.1.1", + "vite-plugin-dts": "^1.7.2" }, "dependencies": { - "rrdom": "^0.1.3", - "rrweb-snapshot": "^2.0.0-alpha.0", - "rrweb": "^2.0.0-alpha.0" + "rrdom": "^0.1.7", + "rrweb-snapshot": "^2.0.0-alpha.4", + "rrweb": "^2.0.0-alpha.4" } } diff --git a/packages/rrweb-cutter/vite.config.ts b/packages/rrweb-cutter/vite.config.ts index e0ac7fe6ef..048a3b541f 100644 --- a/packages/rrweb-cutter/vite.config.ts +++ b/packages/rrweb-cutter/vite.config.ts @@ -1,17 +1,19 @@ -import { defineConfig } from 'vite'; -import { resolve } from 'path'; - -export default defineConfig({ - esbuild: { - minify: true, - }, +import path from 'path'; +import dts from 'vite-plugin-dts'; +/** + * @type {import('vite').UserConfig} + */ +export default { build: { - minify: 'terser', - sourcemap: true, lib: { - entry: resolve(__dirname, 'src/index.ts'), - name: 'rrwebCutter', + entry: path.resolve(__dirname, 'src/index.ts'), + name: 'rrwebTypes', formats: ['es', 'cjs', 'umd', 'iife'], }, + + minify: true, + + sourcemap: true, }, -}); + plugins: [dts()], +}; diff --git a/packages/types/package.json b/packages/types/package.json index a6060cd16f..842120aae0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -15,7 +15,7 @@ "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, - "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/types#readme", + "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/types#readme", "bugs": { "url": "https://github.com/rrweb-io/rrweb/issues" }, @@ -40,8 +40,8 @@ ], "devDependencies": { "typescript": "^4.7.3", - "vite": "^3.2.0-beta.2", - "vite-plugin-dts": "^1.6.6" + "vite": "^4.1.1", + "vite-plugin-dts": "^1.7.2" }, "dependencies": { "rrweb-snapshot": "^2.0.0-alpha.4" diff --git a/yarn.lock b/yarn.lock index 85500f8143..18c892337a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -721,15 +721,115 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/android-arm@0.15.18": - version "0.15.18" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" - integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw== - -"@esbuild/linux-loong64@0.15.18": - version "0.15.18" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" - integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ== +"@esbuild/android-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" + integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== + +"@esbuild/android-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" + integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== + +"@esbuild/android-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" + integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== + +"@esbuild/darwin-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" + integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== + +"@esbuild/darwin-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== + +"@esbuild/freebsd-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" + integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== + +"@esbuild/freebsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" + integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== + +"@esbuild/linux-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" + integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== + +"@esbuild/linux-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" + integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== + +"@esbuild/linux-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" + integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== + +"@esbuild/linux-loong64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" + integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== + +"@esbuild/linux-mips64el@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" + integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== + +"@esbuild/linux-ppc64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" + integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== + +"@esbuild/linux-riscv64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" + integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== + +"@esbuild/linux-s390x@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" + integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== + +"@esbuild/linux-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" + integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + +"@esbuild/netbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" + integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== + +"@esbuild/openbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" + integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== + +"@esbuild/sunos-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" + integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== + +"@esbuild/win32-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" + integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== + +"@esbuild/win32-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" + integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== + +"@esbuild/win32-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" + integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== "@eslint/eslintrc@^1.2.3": version "1.2.3" @@ -5007,327 +5107,101 @@ esbuild-android-64@0.14.38: resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64" integrity sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw== -esbuild-android-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz#7e6394a0e517f738641385aaf553c7e4fb6d1ae3" - integrity sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg== - -esbuild-android-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5" - integrity sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA== - esbuild-android-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz#78acc80773d16007de5219ccce544c036abd50b8" integrity sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA== -esbuild-android-arm64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz#6877566be0f82dd5a43030c0007d06ece7f7c02f" - integrity sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g== - -esbuild-android-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz#9cc0ec60581d6ad267568f29cf4895ffdd9f2f04" - integrity sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ== - esbuild-darwin-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz#e02b1291f629ebdc2aa46fabfacc9aa28ff6aa46" integrity sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA== -esbuild-darwin-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz#ea3caddb707d88f844b1aa1dea5ff3b0a71ef1fd" - integrity sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA== - -esbuild-darwin-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz#428e1730ea819d500808f220fbc5207aea6d4410" - integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg== - esbuild-darwin-arm64@0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz" integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== -esbuild-darwin-arm64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz#4e5eaab54df66cc319b76a2ac0e8af4e6f0d9c2f" - integrity sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA== - -esbuild-darwin-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz#b6dfc7799115a2917f35970bfbc93ae50256b337" - integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA== - esbuild-freebsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz#790b8786729d4aac7be17648f9ea8e0e16475b5e" integrity sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig== -esbuild-freebsd-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz#47b5abc7426eae66861490ffbb380acc67af5b15" - integrity sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA== - -esbuild-freebsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz#4e190d9c2d1e67164619ae30a438be87d5eedaf2" - integrity sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA== - esbuild-freebsd-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz#b66340ab28c09c1098e6d9d8ff656db47d7211e6" integrity sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ== -esbuild-freebsd-arm64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz#e8c54c8637cd44feed967ea12338b0a4da3a7b11" - integrity sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw== - -esbuild-freebsd-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz#18a4c0344ee23bd5a6d06d18c76e2fd6d3f91635" - integrity sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA== - esbuild-linux-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz#7927f950986fd39f0ff319e92839455912b67f70" integrity sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g== -esbuild-linux-32@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz#229cf3246de2b7937c3ac13fac622d4d7a1344c5" - integrity sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ== - -esbuild-linux-32@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz#9a329731ee079b12262b793fb84eea762e82e0ce" - integrity sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg== - esbuild-linux-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz#4893d07b229d9cfe34a2b3ce586399e73c3ac519" integrity sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q== -esbuild-linux-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz#7c0e7226c02c42aacc5656c36977493dc1e96c4f" - integrity sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug== - -esbuild-linux-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz#532738075397b994467b514e524aeb520c191b6c" - integrity sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw== - esbuild-linux-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz#8442402e37d0b8ae946ac616784d9c1a2041056a" integrity sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA== -esbuild-linux-arm64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz#0af1eda474b5c6cc0cace8235b74d0cb8fcf57a7" - integrity sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw== - -esbuild-linux-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz#5372e7993ac2da8f06b2ba313710d722b7a86e5d" - integrity sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug== - esbuild-linux-arm@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz#d5dbf32d38b7f79be0ec6b5fb2f9251fd9066986" integrity sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA== -esbuild-linux-arm@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz#de4d1fa6b77cdcd00e2bb43dd0801e4680f0ab52" - integrity sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw== - -esbuild-linux-arm@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz#e734aaf259a2e3d109d4886c9e81ec0f2fd9a9cc" - integrity sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA== - esbuild-linux-mips64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz#95081e42f698bbe35d8ccee0e3a237594b337eb5" integrity sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ== -esbuild-linux-mips64le@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz#822c1778495f7868e990d4da47ad7281df28fd15" - integrity sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA== - -esbuild-linux-mips64le@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz#c0487c14a9371a84eb08fab0e1d7b045a77105eb" - integrity sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ== - esbuild-linux-ppc64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz#dceb0a1b186f5df679618882a7990bd422089b47" integrity sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q== -esbuild-linux-ppc64le@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz#55de0a9ec4a48fedfe82a63e083164d001709447" - integrity sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ== - -esbuild-linux-ppc64le@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz#af048ad94eed0ce32f6d5a873f7abe9115012507" - integrity sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w== - esbuild-linux-riscv64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz#61fb8edb75f475f9208c4a93ab2bfab63821afd2" integrity sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ== -esbuild-linux-riscv64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz#cd2b7381880b2f4b21a5a598fb673492120f18a5" - integrity sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA== - -esbuild-linux-riscv64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz#423ed4e5927bd77f842bd566972178f424d455e6" - integrity sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg== - esbuild-linux-s390x@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz#34c7126a4937406bf6a5e69100185fd702d12fe0" integrity sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ== -esbuild-linux-s390x@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz#4b319eca2a5c64637fc7397ffbd9671719cdb6bf" - integrity sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g== - -esbuild-linux-s390x@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz#21d21eaa962a183bfb76312e5a01cc5ae48ce8eb" - integrity sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ== - esbuild-netbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz#322ea9937d9e529183ee281c7996b93eb38a5d95" integrity sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q== -esbuild-netbsd-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz#c27cde8b5cb55dcc227943a18ab078fb98d0adbf" - integrity sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw== - -esbuild-netbsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998" - integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg== - esbuild-openbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz#1ca29bb7a2bf09592dcc26afdb45108f08a2cdbd" integrity sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ== -esbuild-openbsd-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz#af5ab2d1cb41f09064bba9465fc8bf1309150df1" - integrity sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA== - -esbuild-openbsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz#79591a90aa3b03e4863f93beec0d2bab2853d0a8" - integrity sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ== - esbuild-sunos-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz#c9446f7d8ebf45093e7bb0e7045506a88540019b" integrity sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA== -esbuild-sunos-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz#db3ae20526055cf6fd5c4582676233814603ac54" - integrity sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA== - -esbuild-sunos-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz#fd528aa5da5374b7e1e93d36ef9b07c3dfed2971" - integrity sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw== - esbuild-windows-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz#f8e9b4602fd0ccbd48e5c8d117ec0ba4040f2ad1" integrity sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw== -esbuild-windows-32@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz#021ffceb0a3f83078262870da88a912293c57475" - integrity sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg== - -esbuild-windows-32@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz#0e92b66ecdf5435a76813c4bc5ccda0696f4efc3" - integrity sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ== - esbuild-windows-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz#280f58e69f78535f470905ce3e43db1746518107" integrity sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw== -esbuild-windows-64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz#a4d3407b580f9faac51f61eec095fa985fb3fee4" - integrity sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA== - -esbuild-windows-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz#0fc761d785414284fc408e7914226d33f82420d0" - integrity sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw== - esbuild-windows-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz#d97e9ac0f95a4c236d9173fa9f86c983d6a53f54" integrity sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw== -esbuild-windows-arm64@0.14.48: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz#762c0562127d8b09bfb70a3c816460742dd82880" - integrity sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g== - -esbuild-windows-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz#5b5bdc56d341d0922ee94965c89ee120a6a86eb7" - integrity sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ== - -esbuild@^0.14.27: - version "0.14.48" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.48.tgz#da5d8d25cd2d940c45ea0cfecdca727f7aee2b85" - integrity sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA== - optionalDependencies: - esbuild-android-64 "0.14.48" - esbuild-android-arm64 "0.14.48" - esbuild-darwin-64 "0.14.48" - esbuild-darwin-arm64 "0.14.48" - esbuild-freebsd-64 "0.14.48" - esbuild-freebsd-arm64 "0.14.48" - esbuild-linux-32 "0.14.48" - esbuild-linux-64 "0.14.48" - esbuild-linux-arm "0.14.48" - esbuild-linux-arm64 "0.14.48" - esbuild-linux-mips64le "0.14.48" - esbuild-linux-ppc64le "0.14.48" - esbuild-linux-riscv64 "0.14.48" - esbuild-linux-s390x "0.14.48" - esbuild-netbsd-64 "0.14.48" - esbuild-openbsd-64 "0.14.48" - esbuild-sunos-64 "0.14.48" - esbuild-windows-32 "0.14.48" - esbuild-windows-64 "0.14.48" - esbuild-windows-arm64 "0.14.48" - esbuild@^0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz" @@ -5354,33 +5228,33 @@ esbuild@^0.14.38: esbuild-windows-64 "0.14.38" esbuild-windows-arm64 "0.14.38" -esbuild@^0.15.9: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d" - integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q== +esbuild@^0.16.14: + version "0.16.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" + integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== optionalDependencies: - "@esbuild/android-arm" "0.15.18" - "@esbuild/linux-loong64" "0.15.18" - esbuild-android-64 "0.15.18" - esbuild-android-arm64 "0.15.18" - esbuild-darwin-64 "0.15.18" - esbuild-darwin-arm64 "0.15.18" - esbuild-freebsd-64 "0.15.18" - esbuild-freebsd-arm64 "0.15.18" - esbuild-linux-32 "0.15.18" - esbuild-linux-64 "0.15.18" - esbuild-linux-arm "0.15.18" - esbuild-linux-arm64 "0.15.18" - esbuild-linux-mips64le "0.15.18" - esbuild-linux-ppc64le "0.15.18" - esbuild-linux-riscv64 "0.15.18" - esbuild-linux-s390x "0.15.18" - esbuild-netbsd-64 "0.15.18" - esbuild-openbsd-64 "0.15.18" - esbuild-sunos-64 "0.15.18" - esbuild-windows-32 "0.15.18" - esbuild-windows-64 "0.15.18" - esbuild-windows-arm64 "0.15.18" + "@esbuild/android-arm" "0.16.17" + "@esbuild/android-arm64" "0.16.17" + "@esbuild/android-x64" "0.16.17" + "@esbuild/darwin-arm64" "0.16.17" + "@esbuild/darwin-x64" "0.16.17" + "@esbuild/freebsd-arm64" "0.16.17" + "@esbuild/freebsd-x64" "0.16.17" + "@esbuild/linux-arm" "0.16.17" + "@esbuild/linux-arm64" "0.16.17" + "@esbuild/linux-ia32" "0.16.17" + "@esbuild/linux-loong64" "0.16.17" + "@esbuild/linux-mips64el" "0.16.17" + "@esbuild/linux-ppc64" "0.16.17" + "@esbuild/linux-riscv64" "0.16.17" + "@esbuild/linux-s390x" "0.16.17" + "@esbuild/linux-x64" "0.16.17" + "@esbuild/netbsd-x64" "0.16.17" + "@esbuild/openbsd-x64" "0.16.17" + "@esbuild/sunos-x64" "0.16.17" + "@esbuild/win32-arm64" "0.16.17" + "@esbuild/win32-ia32" "0.16.17" + "@esbuild/win32-x64" "0.16.17" escalade@^3.1.1: version "3.1.1" @@ -10519,16 +10393,7 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.4.13: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^8.4.18: +postcss@^8.4.21: version "8.4.21" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== @@ -11102,7 +10967,7 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19 is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.22.0, resolve@^1.22.1, resolve@~1.22.1: +resolve@^1.22.1, resolve@~1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -11278,13 +11143,6 @@ rollup@^2.56.3: optionalDependencies: fsevents "~2.3.2" -rollup@^2.59.0: - version "2.75.7" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9" - integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ== - optionalDependencies: - fsevents "~2.3.2" - rollup@^2.68.0: version "2.68.0" resolved "https://registry.npmjs.org/rollup/-/rollup-2.68.0.tgz" @@ -11299,10 +11157,10 @@ rollup@^2.71.1: optionalDependencies: fsevents "~2.3.2" -rollup@^2.79.1: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== +rollup@^3.10.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.15.0.tgz#6f4105e8c4b8145229657b74ad660b02fbfacc05" + integrity sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg== optionalDependencies: fsevents "~2.3.2" @@ -12827,7 +12685,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vite-plugin-dts@^1.6.6: +vite-plugin-dts@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-1.7.2.tgz#75c581b5c015a5b437d213454e1fd2bc94223b43" integrity sha512-55Jwfv6n8gAlRSVGCpIY13TCqadtaKex9d2mCbaSxMFAU06suMDsFhIch1x55eptpC2RpeiresqbTjhN2HSEtQ== @@ -12841,27 +12699,15 @@ vite-plugin-dts@^1.6.6: kolorist "^1.6.0" ts-morph "17.0.1" -vite@^2.9.13: - version "2.9.13" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc" - integrity sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw== - dependencies: - esbuild "^0.14.27" - postcss "^8.4.13" - resolve "^1.22.0" - rollup "^2.59.0" - optionalDependencies: - fsevents "~2.3.2" - -vite@^3.2.0-beta.2: - version "3.2.5" - resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.5.tgz#dee5678172a8a0ab3e547ad4148c3d547f90e86a" - integrity sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ== +vite@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.1.1.tgz#3b18b81a4e85ce3df5cbdbf4c687d93ebf402e6b" + integrity sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg== dependencies: - esbuild "^0.15.9" - postcss "^8.4.18" + esbuild "^0.16.14" + postcss "^8.4.21" resolve "^1.22.1" - rollup "^2.79.1" + rollup "^3.10.0" optionalDependencies: fsevents "~2.3.2" From 1caade6cacf52137757059e49cb82876be913a2a Mon Sep 17 00:00:00 2001 From: YunFeng0817 Date: Mon, 13 Feb 2023 06:22:33 +0000 Subject: [PATCH 20/52] Apply formatting changes --- packages/rrweb/src/replay/sync-replayer.ts | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index 76517ac457..07b7f44704 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -275,7 +275,7 @@ export class SyncReplayer { for (const plugin of this.config.plugins || []) { if (plugin.handler) plugin.handler(event, true, { - replayer: (this as unknown) as Replayer, + replayer: this as unknown as Replayer, }); } @@ -296,20 +296,20 @@ export class SyncReplayer { this.legacy_missingNodeRetryMap = {}; const collected: AppendedIframe[] = []; rebuild(event.data.node, { - doc: (this.virtualDom as unknown) as Document, + doc: this.virtualDom as unknown as Document, afterAppend: (builtNode) => { this.collectIframeAndAttachDocument( collected, - (builtNode as unknown) as RRNode, + builtNode as unknown as RRNode, ); }, cache: this.cache, - mirror: (this.mirror as unknown) as Mirror, + mirror: this.mirror as unknown as Mirror, }); for (const { mutationInQueue, builtNode } of collected) { this.attachDocumentToIframe( mutationInQueue, - (builtNode as unknown) as RRIFrameElement, + builtNode as unknown as RRIFrameElement, ); this.newDocumentQueue = this.newDocumentQueue.filter( (m) => m !== mutationInQueue, @@ -324,8 +324,8 @@ export class SyncReplayer { ) { const collected: AppendedIframe[] = []; buildNodeWithSN(mutation.node, { - doc: (iframeEl.contentDocument as unknown) as Document, - mirror: (this.mirror as unknown) as Mirror, + doc: iframeEl.contentDocument as unknown as Document, + mirror: this.mirror as unknown as Mirror, hackCss: true, skipChild: false, cache: this.cache, @@ -344,8 +344,8 @@ export class SyncReplayer { ) { if ( isSerializedIframe( - (builtNode as unknown) as Node, - (this.mirror as unknown) as IMirror, + builtNode as unknown as Node, + this.mirror as unknown as IMirror, ) ) { const mutationInQueue = this.newDocumentQueue.find( @@ -539,7 +539,7 @@ export class SyncReplayer { if (!parent) { return this.warnNodeNotFound(d, mutation.parentId); } - if (mutation.isShadow && hasShadowRoot((parent as unknown) as Node)) { + if (mutation.isShadow && hasShadowRoot(parent as unknown as Node)) { parent = (parent as RRElement).shadowRoot; } // target may be removed with its parents before @@ -606,7 +606,7 @@ export class SyncReplayer { } if (mutation.node.isShadow) { // If the parent is attached a shadow dom after it's created, it won't have a shadow root. - if (!hasShadowRoot((parent as unknown) as Node)) { + if (!hasShadowRoot(parent as unknown as Node)) { (parent as RRElement).attachShadow({ mode: 'open' }); parent = (parent as RRElement).shadowRoot!; } else parent = (parent as RRElement).shadowRoot!; @@ -637,13 +637,13 @@ export class SyncReplayer { this.attachDocumentToIframe(mutation, parent as RRIFrameElement); return; } - const target = (buildNodeWithSN(mutation.node, { - doc: (targetDoc as unknown) as Document, // can be Document or RRDocument - mirror: (mirror as unknown) as Mirror, + const target = buildNodeWithSN(mutation.node, { + doc: targetDoc as unknown as Document, // can be Document or RRDocument + mirror: mirror as unknown as Mirror, skipChild: true, hackCss: true, cache: this.cache, - }) as unknown) as RRNode; + }) as unknown as RRNode; // legacy data, we should not have -1 siblings any more if (mutation.previousId === -1 || mutation.nextId === -1) { From 0164770ce2d4edf23fa24434d6b0d60c3d42deab Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 13 Feb 2023 22:01:11 +1100 Subject: [PATCH 21/52] update sync-replayer with the new version of Replayer --- packages/rrweb/src/index.ts | 2 - packages/rrweb/src/replay/sync-replayer.ts | 352 +++++++++++++++------ 2 files changed, 253 insertions(+), 101 deletions(-) diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index 3afbacb651..ecc1695909 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -1,6 +1,5 @@ import record from './record'; import { Replayer } from './replay'; -import { SyncReplayer } from './replay/sync-replayer'; import { _mirror } from './utils'; import * as utils from './utils'; @@ -21,7 +20,6 @@ export { addCustomEvent, freezePage, Replayer, - SyncReplayer, _mirror as mirror, utils, }; diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index 07b7f44704..0c4369580e 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -4,10 +4,12 @@ import { NodeType, BuildCache, createCache, - IMirror, Mirror, + attributes, + serializedElementNodeWithId, + IMirror, } from 'rrweb-snapshot'; -import { RRDocument as BaseRRDocument, StyleRuleType } from 'rrdom'; +import { RRDocument as BaseRRDocument } from 'rrdom'; import type { RRNode, RRElement, @@ -16,29 +18,33 @@ import type { RRMediaElement, RRCanvasElement, Mirror as RRDOMMirror, - VirtualStyleRules, } from 'rrdom'; import * as mittProxy from 'mitt'; +import type { playerConfig } from '../types'; import { + EventType, + IncrementalSource, fullSnapshotEvent, eventWithTime, - playerConfig, + MouseInteractions, playerMetaData, addedNodeMutation, incrementalSnapshotEvent, incrementalData, + ReplayerEvents, Handler, Emitter, + MediaInteractions, metaEvent, mutationData, styleValueWithPriority, mouseMovePos, canvasEventWithTime, - MediaInteractions, - ReplayerEvents, - EventType, - IncrementalSource, -} from '../types'; + selectionData, + styleSheetRuleData, + styleDeclarationData, + adoptedStyleSheetData, +} from '@rrweb/types'; import { queueToResolveTrees, iterateResolveTree, @@ -74,6 +80,18 @@ export class SyncReplayer { public mousePos: mouseMovePos | null = null; + // In the fast-forward mode, only the last selection data needs to be applied. + public lastSelectionData: selectionData | null = null; + + // In the fast-forward mode using VirtualDom optimization, all stylesheetRule, and styleDeclaration events on constructed StyleSheets will be delayed to get applied until the flush stage. + public constructedStyleMutations: ( + | styleSheetRuleData + | styleDeclarationData + )[] = []; + + // Similar to the reason for constructedStyleMutations. + public adoptedStyleSheets: adoptedStyleSheetData[] = []; + public events: eventWithTime[]; public latestMetaEvent: eventWithTime | null = null; @@ -84,7 +102,6 @@ export class SyncReplayer { private emitter: Emitter = mitt(); - // tslint:disable-next-line: variable-name private legacy_missingNodeRetryMap: missingNodeMap = {}; // The replayer uses the cache to speed up replay and scrubbing. @@ -98,7 +115,7 @@ export class SyncReplayer { events: Array, config?: Partial, ) { - if (events.length < 2) { + if (!config?.liveMode && events.length < 2) { throw new Error('Replayer need at least 2 events.'); } this.events = events @@ -109,15 +126,22 @@ export class SyncReplayer { return e as eventWithTime; }) .sort((a1, a2) => a1.timestamp - a2.timestamp); - this.getCastFn = this.getCastFn.bind(this); - const defaultConfig = { showWarning: true, showDebug: false, liveMode: false, + logger: console, }; this.config = Object.assign({}, defaultConfig, config); + /** + * Exposes mirror to the plugins + */ + for (const plugin of this.config.plugins || []) { + if (plugin.getMirror) + plugin.getMirror({ nodeMirror: this.mirror as unknown as Mirror }); + } + this.emitter.on(ReplayerEvents.PlayBack, () => { this.mirror.reset(); }); @@ -221,6 +245,27 @@ export class SyncReplayer { } } + /** + * Totally destroy this replayer and please be careful that this operation is irreversible. + * Memory occupation can be released by removing all references to this replayer. + */ + public destroy() { + this.emitter.emit(ReplayerEvents.Destroy); + } + + public startLive() { + this.config.liveMode = true; + this.play(); + } + + public addEvent(rawEvent: eventWithTime | string) { + const event = this.config.unpackFn + ? this.config.unpackFn(rawEvent as string) + : (rawEvent as eventWithTime); + const castFn = this.getCastFn(event); + castFn(); + } + /** * Empties the replayer's cache and reclaims memory. * The replayer will use this cache to speed up the playback. @@ -229,7 +274,7 @@ export class SyncReplayer { this.cache = createCache(); } - private getCastFn(event: eventWithTime) { + private getCastFn = (event: eventWithTime) => { let castFn: undefined | (() => void); switch (event.type) { case EventType.DomContentLoaded: @@ -279,38 +324,55 @@ export class SyncReplayer { }); } + // events are kept sorted by timestamp, check if this is the last event + if ( + !this.config.liveMode && + event === this.events[this.events.length - 1] + ) { + this.emitter.emit(ReplayerEvents.Finish); + } + this.emitter.emit(ReplayerEvents.EventCast, event); }; return wrappedCastFn; - } + }; private rebuildFullSnapshot( event: fullSnapshotEvent & { timestamp: number }, ) { if (Object.keys(this.legacy_missingNodeRetryMap).length) { - console.warn( + this.warn( 'Found unresolved missing node map', this.legacy_missingNodeRetryMap, ); } this.legacy_missingNodeRetryMap = {}; const collected: AppendedIframe[] = []; + const afterAppend = (builtNode: Node, id: number) => { + this.collectIframeAndAttachDocument( + collected, + builtNode as unknown as RRNode, + ); + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) + plugin.onBuild(builtNode, { + id, + replayer: this as unknown as Replayer, + }); + } + }; + + this.mirror.reset(); rebuild(event.data.node, { doc: this.virtualDom as unknown as Document, - afterAppend: (builtNode) => { - this.collectIframeAndAttachDocument( - collected, - builtNode as unknown as RRNode, - ); - }, + afterAppend, cache: this.cache, mirror: this.mirror as unknown as Mirror, }); + afterAppend(this.virtualDom as unknown as Document, event.data.node.id); + for (const { mutationInQueue, builtNode } of collected) { - this.attachDocumentToIframe( - mutationInQueue, - builtNode as unknown as RRIFrameElement, - ); + this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( (m) => m !== mutationInQueue, ); @@ -323,13 +385,34 @@ export class SyncReplayer { iframeEl: RRIFrameElement, ) { const collected: AppendedIframe[] = []; + const afterAppend = (builtNode: Node, id: number) => { + this.collectIframeAndAttachDocument( + collected, + builtNode as unknown as RRNode, + ); + + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) + plugin.onBuild(builtNode, { + id, + replayer: this as unknown as Replayer, + }); + } + }; + buildNodeWithSN(mutation.node, { doc: iframeEl.contentDocument as unknown as Document, mirror: this.mirror as unknown as Mirror, hackCss: true, skipChild: false, + afterAppend, cache: this.cache, }); + afterAppend( + iframeEl.contentDocument as unknown as Document, + mutation.node.id, + ); + for (const { mutationInQueue, builtNode } of collected) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( @@ -369,6 +452,7 @@ export class SyncReplayer { try { this.applyMutation(d); } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions this.warn(`Exception in mutation ${error.message || error}`, d); } break; @@ -386,8 +470,43 @@ export class SyncReplayer { }; } break; - case IncrementalSource.MouseInteraction: + case IncrementalSource.MouseInteraction: { + /** + * Same as the situation of missing input target. + */ + if (d.id === -1) { + break; + } + + const target = this.mirror.getNode(d.id); + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + this.emitter.emit(ReplayerEvents.MouseInteraction, { + type: d.type, + target, + }); + switch (d.type) { + case MouseInteractions.Blur: + break; + case MouseInteractions.Focus: + break; + case MouseInteractions.Click: + case MouseInteractions.TouchStart: + case MouseInteractions.TouchEnd: + this.mousePos = { + x: d.x, + y: d.y, + id: d.id, + debugData: d, + }; + break; + case MouseInteractions.TouchCancel: + break; + default: + } break; + } case IncrementalSource.Scroll: { /** * Same as the situation of missing input target. @@ -433,13 +552,13 @@ export class SyncReplayer { } const mediaEl = target as RRMediaElement; try { - if (d.currentTime) { + if (d.currentTime !== undefined) { mediaEl.currentTime = d.currentTime; } - if (d.volume) { + if (d.volume !== undefined) { mediaEl.volume = d.volume; } - if (d.muted) { + if (d.muted !== undefined) { mediaEl.muted = d.muted; } if (d.type === MediaInteractions.Pause) { @@ -452,52 +571,22 @@ export class SyncReplayer { // unexpeted behavior void mediaEl.play(); } - } catch (error) { - if (this.config.showWarning) { - console.warn( - `Failed to replay media interactions: ${error.message || error}`, - ); + if (d.type === MediaInteractions.RateChange) { + mediaEl.playbackRate = d.playbackRate; } + } catch (error) { + this.warn( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions + `Failed to replay media interactions: ${error.message || error}`, + ); } break; } - case IncrementalSource.StyleSheetRule: { - const target = this.virtualDom.mirror.getNode(d.id) as RRStyleElement; - if (!target) { - return this.debugNodeNotFound(d, d.id); - } - const rules: VirtualStyleRules = target.rules; - d.adds?.forEach(({ rule, index: nestedIndex }) => - rules?.push({ - cssText: rule, - index: nestedIndex, - type: StyleRuleType.Insert, - }), - ); - d.removes?.forEach(({ index: nestedIndex }) => - rules?.push({ index: nestedIndex, type: StyleRuleType.Remove }), - ); - break; - } + case IncrementalSource.StyleSheetRule: case IncrementalSource.StyleDeclaration: { - const target = this.mirror.getNode(d.id) as RRStyleElement; - if (!target) { - return this.debugNodeNotFound(d, d.id); - } - const rules: VirtualStyleRules = target.rules; - d.set && - rules.push({ - type: StyleRuleType.SetProperty, - index: d.index, - ...d.set, - }); - d.remove && - rules.push({ - type: StyleRuleType.RemoveProperty, - index: d.index, - ...d.remove, - }); - + if (d.styleId) this.constructedStyleMutations.push(d); + else if (d.id) + (this.mirror.getNode(d.id) as RRStyleElement | null)?.rules.push(d); break; } case IncrementalSource.CanvasMutation: { @@ -519,6 +608,14 @@ export class SyncReplayer { this.unhandledEvents.push(e); break; } + case IncrementalSource.Selection: { + this.lastSelectionData = d; + break; + } + case IncrementalSource.AdoptedStyleSheet: { + this.adoptedStyleSheets.push(d); + break; + } default: } } @@ -571,8 +668,7 @@ export class SyncReplayer { } }); - // tslint:disable-next-line: variable-name - const legacy_missingNodeMap = { + const legacy_missingNodeMap: missingNodeMap = { ...this.legacy_missingNodeRetryMap, }; const queue: addedNodeMutation[] = []; @@ -608,7 +704,9 @@ export class SyncReplayer { // If the parent is attached a shadow dom after it's created, it won't have a shadow root. if (!hasShadowRoot(parent as unknown as Node)) { (parent as RRElement).attachShadow({ mode: 'open' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion parent = (parent as RRElement).shadowRoot!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion } else parent = (parent as RRElement).shadowRoot!; } @@ -631,18 +729,29 @@ export class SyncReplayer { const targetDoc = mutation.node.rootId ? mirror.getNode(mutation.node.rootId) : this.virtualDom; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (isSerializedIframe(parent, mirror)) { + + if (isSerializedIframe(parent, mirror)) { this.attachDocumentToIframe(mutation, parent as RRIFrameElement); return; } + const afterAppend = (node: Node | RRNode, id: number) => { + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) + plugin.onBuild(node, { id, replayer: this as unknown as Replayer }); + } + }; + const target = buildNodeWithSN(mutation.node, { - doc: targetDoc as unknown as Document, // can be Document or RRDocument + doc: targetDoc as unknown as Document, mirror: mirror as unknown as Mirror, skipChild: true, hackCss: true, cache: this.cache, + /** + * caveat: `afterAppend` only gets called on child nodes of target + * we have to call it again below when this target was added to the DOM + */ + afterAppend, }) as unknown as RRNode; // legacy data, we should not have -1 siblings any more @@ -663,11 +772,33 @@ export class SyncReplayer { ) { // https://github.com/rrweb-io/rrweb/issues/745 // parent is textarea, will only keep one child node as the value - for (const c of Array.from(parent.childNodes)) { + for (const c of parent.childNodes) { if (c.nodeType === parent.TEXT_NODE) { parent.removeChild(c); } } + } else if (parentSn?.type === NodeType.Document) { + /** + * Sometimes the document object is changed or reopened and the MutationObserver is disconnected, so the removal of child elements can't be detected and recorded. + * After the change of document, we may get another mutation which adds a new doctype or a HTML element, while the old one still exists in the dom. + * So, we need to remove the old one first to avoid collision. + */ + const parentDoc = parent as RRDocument; + /** + * To detect the exist of the old doctype before adding a new doctype. + * We need to remove the old doctype before adding the new one. Otherwise, code will throw "mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed". + */ + if ( + mutation.node.type === NodeType.DocumentType && + parentDoc.childNodes[0]?.nodeType === Node.DOCUMENT_TYPE_NODE + ) + parentDoc.removeChild(parentDoc.childNodes[0]); + /** + * To detect the exist of the old HTML element before adding a new HTML element. + * The reason is similar to the above. One document only allows exactly one DocType and one HTML Element. + */ + if (target.nodeName === 'HTML' && parentDoc.documentElement) + parentDoc.removeChild(parentDoc.documentElement); } if (previous && previous.nextSibling && previous.nextSibling.parentNode) { @@ -679,17 +810,13 @@ export class SyncReplayer { ? parent.insertBefore(target, next) : parent.insertBefore(target, null); } else { - /** - * Sometimes the document changes and the MutationObserver is disconnected, so the removal of child elements can't be detected and recorded. After the change of document, we may get another mutation which adds a new html element, while the old html element still exists in the dom, and we need to remove the old html element first to avoid collision. - */ - if (parent === targetDoc) { - while (targetDoc.firstChild) { - targetDoc.removeChild(targetDoc.firstChild); - } - } - parent.appendChild(target); } + /** + * target was added, execute plugin hooks + */ + afterAppend(target, mutation.node.id); + /** * https://github.com/rrweb-io/rrweb/pull/887 * Remove any virtual style rules for stylesheets if a new text node is appended. @@ -701,8 +828,6 @@ export class SyncReplayer { ) (parent as RRStyleElement).rules = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore if (isSerializedIframe(target, this.mirror)) { const targetId = this.mirror.getId(target); const mutationInQueue = this.newDocumentQueue.find( @@ -798,14 +923,44 @@ export class SyncReplayer { (target as RRElement).removeAttribute(attributeName); } else if (typeof value === 'string') { try { + // When building snapshot, some link styles haven't loaded. Then they are loaded, they will be inlined as incremental mutation change of attribute. We need to replace the old elements whose styles aren't inlined. + if ( + attributeName === '_cssText' && + (target.nodeName === 'LINK' || target.nodeName === 'STYLE') + ) { + try { + const newSn = mirror.getMeta( + target, + ) as serializedElementNodeWithId; + Object.assign( + newSn.attributes, + mutation.attributes as attributes, + ); + const newNode = buildNodeWithSN(newSn, { + doc: target.ownerDocument as unknown as Document, + mirror: mirror as unknown as Mirror, + skipChild: true, + hackCss: true, + cache: this.cache, + }) as unknown as RRNode; + const siblingNode = target.nextSibling; + const parentNode = target.parentNode; + if (newNode && parentNode) { + parentNode.removeChild(target); + parentNode.insertBefore(newNode, siblingNode); + mirror.replace(mutation.id, newNode as Node & RRNode); + break; + } + } catch (e) { + // for safe + } + } (target as RRElement).setAttribute(attributeName, value); } catch (error) { - if (this.config.showWarning) { - console.warn( - 'An error occurred may due to the checkout feature.', - error, - ); - } + this.warn( + 'An error occurred may due to the checkout feature.', + error, + ); } } else if (attributeName === 'style') { const styleValues = value; @@ -867,21 +1022,20 @@ export class SyncReplayer { * is microtask, so events fired on a removed DOM may emit * snapshots in the reverse order. */ - this.debug(REPLAY_CONSOLE_PREFIX, `Node with id '${id}' not found. `, d); + this.debug(`Node with id '${id}' not found. `, d); } private warn(...args: Parameters) { if (!this.config.showWarning) { return; } - console.warn(REPLAY_CONSOLE_PREFIX, ...args); + this.config.logger?.warn(REPLAY_CONSOLE_PREFIX, ...args); } private debug(...args: Parameters) { if (!this.config.showDebug) { return; } - // tslint:disable-next-line: no-console - console.log(REPLAY_CONSOLE_PREFIX, ...args); + this.config.logger?.log(REPLAY_CONSOLE_PREFIX, ...args); } } From e20974804e96e0ee2940f2cd8b0a1e99e9666f90 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 00:47:13 +1100 Subject: [PATCH 22/52] fix import and upgrade jest version --- packages/rrweb-cutter/jest.config.js | 5 +- packages/rrweb-cutter/package.json | 18 +- packages/rrweb-cutter/src/index.ts | 8 +- packages/rrweb-cutter/src/snapshot.ts | 61 +- .../__snapshots__/session-cutter.test.ts.snap | 446 +++--- .../test/events/inline-style.event.ts | 2 +- .../test/events/mutation.event.ts | 2 +- .../rrweb-cutter/test/session-cutter.test.ts | 16 +- packages/rrweb-cutter/test/unit.test.ts | 6 +- packages/rrweb-cutter/test/utils.ts | 15 - packages/rrweb-cutter/tsconfig.json | 3 +- packages/rrweb-cutter/vite.config.ts | 3 +- packages/rrweb/package.json | 17 + yarn.lock | 1218 ++++++++++++++++- 14 files changed, 1501 insertions(+), 319 deletions(-) delete mode 100644 packages/rrweb-cutter/test/utils.ts diff --git a/packages/rrweb-cutter/jest.config.js b/packages/rrweb-cutter/jest.config.js index e86e13bab9..81f6951715 100644 --- a/packages/rrweb-cutter/jest.config.js +++ b/packages/rrweb-cutter/jest.config.js @@ -1,5 +1,8 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { +export default { preset: 'ts-jest', testEnvironment: 'node', + moduleNameMapper: { + 'rrweb/test/utils': '/../rrweb/test/utils', + }, }; diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index 04e6fbe005..e70cd7322f 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -26,28 +26,24 @@ ], "license": "MIT", "type": "module", - "main": "dist/index.umd.cjs", + "main": "dist/index.cjs", "unpkg": "dist/index.iife.js", - "module": "dist/index.es.js", + "module": "dist/index.js", "typings": "dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.es.js", - "require": "./dist/index.umd.cjs" - } - }, "files": [ "dist" ], "devDependencies": { - "@types/jest": "^27.4.1", - "jest": "^27.5.1", - "ts-jest": "^27.1.3", + "@types/jest": "^29.4.0", + "jest": "29.4.2", + "jest-environment-jsdom": "^29.4.2", + "ts-jest": "^29.0.5", "typescript": "^4.7.3", "vite": "^4.1.1", "vite-plugin-dts": "^1.7.2" }, "dependencies": { + "@rrweb/types": "^2.0.0-alpha.4", "rrdom": "^0.1.7", "rrweb-snapshot": "^2.0.0-alpha.4", "rrweb": "^2.0.0-alpha.4" diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index f1346d5a36..e3fdc7909d 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -2,9 +2,10 @@ import type { addedNodeMutation, eventWithTime, mousePosition, -} from 'rrweb/typings/types'; +} from '@rrweb/types'; import { IncrementalSource } from 'rrweb'; -import { EventType, SyncReplayer } from 'rrweb'; +import { EventType } from 'rrweb'; +import { SyncReplayer } from 'rrweb/sync-replayer'; import snapshot from './snapshot'; import { serializedNodeWithId } from 'rrweb-snapshot'; type CutterConfig = { @@ -172,7 +173,8 @@ export function pruneBranches( }, } as eventWithTime); } else if ('id' in event.data) { - if (treeSet.has(event.data.id)) result.push(event); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (treeSet.has(event.data.id!)) result.push(event); } else if (event.data.source === IncrementalSource.Mutation) { const { removes, adds, texts, attributes } = event.data; const prunedRemoves = removes.filter((remove) => diff --git a/packages/rrweb-cutter/src/snapshot.ts b/packages/rrweb-cutter/src/snapshot.ts index a7184fa2c8..6cb702bb50 100644 --- a/packages/rrweb-cutter/src/snapshot.ts +++ b/packages/rrweb-cutter/src/snapshot.ts @@ -1,20 +1,20 @@ -import { elementNode, NodeType, serializedNode } from 'rrweb-snapshot'; import type { serializedNodeWithId, attributes } from 'rrweb-snapshot'; -import { Mirror } from 'rrdom'; +import { elementNode, NodeType, serializedNode } from 'rrweb-snapshot'; import type { - RRNode, - RRMediaElement, - RRElement, - RRDocument, - RRDocumentType, + IRRComment, + IRRDocument, + IRRDocumentType, + IRRElement, + IRRNode, RRIFrameElement, - RRComment, + RRMediaElement, } from 'rrdom'; +import { Mirror } from 'rrdom'; function serializeNode( - n: RRNode, + n: IRRNode, options: { - doc: RRDocument; + doc: IRRDocument; mirror: Mirror; }, ): serializedNode | false { @@ -23,11 +23,11 @@ function serializeNode( const rootId = getRootId(doc, mirror); switch (n.RRNodeType) { case NodeType.Document: - if ((n as RRDocument).compatMode !== 'CSS1Compat') { + if ((n as IRRDocument).compatMode !== 'CSS1Compat') { return { type: NodeType.Document, childNodes: [], - compatMode: (n as RRDocument).compatMode, // probably "BackCompat" + compatMode: (n as IRRDocument).compatMode, // probably "BackCompat" rootId, }; } else { @@ -40,19 +40,20 @@ function serializeNode( case NodeType.DocumentType: return { type: NodeType.DocumentType, - name: (n as RRDocumentType).name, - publicId: (n as RRDocumentType).publicId, - systemId: (n as RRDocumentType).systemId, + name: (n as IRRDocumentType).name, + publicId: (n as IRRDocumentType).publicId, + systemId: (n as IRRDocumentType).systemId, rootId, }; case NodeType.Element: - return serializeElementNode(n as RRElement, { + return serializeElementNode(n as IRRElement, { doc, mirror, rootId, }); case NodeType.Text: { - const parentTagName = n.parentNode && (n.parentNode as RRElement).tagName; + const parentTagName = + n.parentNode && (n.parentNode as IRRElement).tagName; const isStyle = parentTagName === 'STYLE' ? true : undefined; return { type: NodeType.Text, @@ -70,7 +71,7 @@ function serializeNode( case NodeType.Comment: return { type: NodeType.Comment, - textContent: (n as RRComment).textContent || '', + textContent: (n as IRRComment).textContent || '', rootId, }; default: @@ -78,13 +79,13 @@ function serializeNode( } } -function getRootId(doc: RRDocument, mirror: Mirror): number | undefined { +function getRootId(doc: IRRDocument, mirror: Mirror): number | undefined { if (!mirror.hasNode(doc)) return undefined; const docId = mirror.getId(doc); return docId === 1 ? undefined : docId; } -function getValidTagName(element: RRElement): string { +function getValidTagName(element: IRRElement): string { const processedTagName = element.tagName.toLowerCase().trim(); const tagNameRegex = new RegExp('[^a-z0-9-_:]'); if (tagNameRegex.test(processedTagName)) { @@ -98,9 +99,9 @@ function getValidTagName(element: RRElement): string { } function serializeElementNode( - n: RRElement, + n: IRRElement, options: { - doc: RRDocument; + doc: IRRDocument; mirror: Mirror; rootId: number | undefined; }, @@ -120,7 +121,7 @@ function serializeElementNode( ) { // the child text is inserted and untracked by the rrweb replayer if (n.childNodes[0] && mirror.getId(n.childNodes[0]) < 0) - attributes._cssText = n.textContent; + attributes._cssText = n.textContent || ''; } if (n.scrollLeft) { attributes.rr_scrollLeft = n.scrollLeft; @@ -150,12 +151,12 @@ function serializeElementNode( } export function serializeNodeWithId( - n: RRNode, + n: IRRNode, options: { - doc: RRDocument; + doc: IRRDocument; mirror: Mirror; skipChild: boolean; - onSerialize?: (n: RRNode) => unknown; + onSerialize?: (n: IRRNode) => unknown; onIframeLoad?: ( iframeNode: RRIFrameElement, node: serializedNodeWithId, @@ -212,9 +213,9 @@ export function serializeNodeWithId( } } - if (n.RRNodeType === NodeType.Element && (n as RRElement).shadowRoot) { + if (n.RRNodeType === NodeType.Element && (n as IRRElement).shadowRoot) { serializedNode.isShadowHost = true; - const shadowRoot = (n as RRElement).shadowRoot; + const shadowRoot = (n as IRRElement).shadowRoot; if (shadowRoot) { bypassOptions.isShadowDom = true; for (const childN of Array.from(shadowRoot.childNodes)) { @@ -255,11 +256,11 @@ export function serializeNodeWithId( } export function snapshot( - n: RRDocument, + n: IRRDocument, options?: { mirror?: Mirror; skipChild?: boolean; - onSerialize?: (n: RRNode) => unknown; + onSerialize?: (n: IRRNode) => unknown; onIframeLoad?: ( iframeNode: RRIFrameElement, node: serializedNodeWithId, diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index d113052141..a8f9628df9 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -5,120 +5,120 @@ exports[`pruneBranches should cut branches that doesn't include id: pruned all b -1 RRDocumentType 2 HTML 12 BODY - 14 DIV id=\\"root\\" - 15 RRText text=\\" \\\\n \\" + 14 DIV id="root" + 15 RRText text=" \\n " " `; exports[`pruneBranches should keep branches where target child nodes was, and gets moved to 1`] = ` "[ { - \\"type\\": 0, - \\"data\\": {} + "type": 0, + "data": {} }, { - \\"type\\": 1, - \\"data\\": {} + "type": 1, + "data": {} }, { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1512, - \\"height\\": 395 + "type": 4, + "data": { + "href": "about:blank", + "width": 1512, + "height": 395 } }, { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ + "type": 2, + "data": { + "node": { + "type": 0, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"head\\", - \\"attributes\\": {}, - \\"childNodes\\": [ + "type": 2, + "tagName": "head", + "attributes": {}, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"meta\\", - \\"attributes\\": { - \\"charset\\": \\"utf-8\\" + "type": 2, + "tagName": "meta", + "attributes": { + "charset": "utf-8" }, - \\"childNodes\\": [], - \\"id\\": 5 + "childNodes": [], + "id": 5 } ], - \\"id\\": 3 + "id": 3 }, { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"childNodes\\": [ + "type": 2, + "tagName": "body", + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"div\\", - \\"attributes\\": { - \\"id\\": \\"root\\" + "type": 2, + "tagName": "div", + "attributes": { + "id": "root" }, - \\"childNodes\\": [ + "childNodes": [ { - \\"type\\": 3, - \\"textContent\\": \\" \\\\n \\", - \\"id\\": 15 + "type": 3, + "textContent": " \\n ", + "id": 15 } ], - \\"id\\": 14 + "id": 14 } ], - \\"id\\": 12 + "id": 12 } ], - \\"id\\": 2 + "id": 2 } ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 + "compatMode": "BackCompat", + "id": 1 }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 + "initialOffset": { + "left": 0, + "top": 0 } } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 15 + "type": 3, + "data": { + "source": 2, + "type": 2, + "id": 15 } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [ + "type": 3, + "data": { + "source": 0, + "texts": [], + "attributes": [], + "removes": [ { - \\"parentId\\": 14, - \\"id\\": 15 + "parentId": 14, + "id": 15 } ], - \\"adds\\": [ + "adds": [ { - \\"parentId\\": 5, - \\"nextId\\": null, - \\"node\\": { - \\"type\\": 3, - \\"textContent\\": \\" \\\\n \\", - \\"id\\": 15 + "parentId": 5, + "nextId": null, + "node": { + "type": 3, + "textContent": " \\n ", + "id": 15 } } ] @@ -130,84 +130,84 @@ exports[`pruneBranches should keep branches where target child nodes was, and ge exports[`pruneBranches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` "[ { - \\"type\\": 0, - \\"data\\": {} + "type": 0, + "data": {} }, { - \\"type\\": 1, - \\"data\\": {} + "type": 1, + "data": {} }, { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1512, - \\"height\\": 395 + "type": 4, + "data": { + "href": "about:blank", + "width": 1512, + "height": 395 } }, { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ + "type": 2, + "data": { + "node": { + "type": 0, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"childNodes\\": [ + "type": 2, + "tagName": "body", + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"div\\", - \\"attributes\\": { - \\"id\\": \\"root\\" + "type": 2, + "tagName": "div", + "attributes": { + "id": "root" }, - \\"childNodes\\": [], - \\"id\\": 14 + "childNodes": [], + "id": 14 } ], - \\"id\\": 12 + "id": 12 } ], - \\"id\\": 2 + "id": 2 } ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 + "compatMode": "BackCompat", + "id": 1 }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 + "initialOffset": { + "left": 0, + "top": 0 } } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [], - \\"adds\\": [ + "type": 3, + "data": { + "source": 0, + "texts": [], + "attributes": [], + "removes": [], + "adds": [ { - \\"parentId\\": 14, - \\"nextId\\": null, - \\"node\\": { - \\"id\\": 98, - \\"type\\": 2, - \\"tagName\\": \\"main\\", - \\"attributes\\": {}, - \\"childNodes\\": [ + "parentId": 14, + "nextId": null, + "node": { + "id": 98, + "type": 2, + "tagName": "main", + "attributes": {}, + "childNodes": [ { - \\"id\\": 99, - \\"type\\": 2, - \\"tagName\\": \\"canvas\\", - \\"attributes\\": {}, - \\"childNodes\\": [] + "id": 99, + "type": 2, + "tagName": "canvas", + "attributes": {}, + "childNodes": [] } ] } @@ -221,78 +221,78 @@ exports[`pruneBranches should remove branches based on child nodes that came in exports[`pruneBranches should remove branches based on nodes that came in after fullsnapshot 1`] = ` "[ { - \\"type\\": 0, - \\"data\\": {} + "type": 0, + "data": {} }, { - \\"type\\": 1, - \\"data\\": {} + "type": 1, + "data": {} }, { - \\"type\\": 4, - \\"data\\": { - \\"href\\": \\"about:blank\\", - \\"width\\": 1512, - \\"height\\": 395 + "type": 4, + "data": { + "href": "about:blank", + "width": 1512, + "height": 395 } }, { - \\"type\\": 2, - \\"data\\": { - \\"node\\": { - \\"type\\": 0, - \\"childNodes\\": [ + "type": 2, + "data": { + "node": { + "type": 0, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"html\\", - \\"attributes\\": {}, - \\"childNodes\\": [ + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"childNodes\\": [ + "type": 2, + "tagName": "body", + "childNodes": [ { - \\"type\\": 2, - \\"tagName\\": \\"div\\", - \\"attributes\\": { - \\"id\\": \\"root\\" + "type": 2, + "tagName": "div", + "attributes": { + "id": "root" }, - \\"childNodes\\": [], - \\"id\\": 14 + "childNodes": [], + "id": 14 } ], - \\"id\\": 12 + "id": 12 } ], - \\"id\\": 2 + "id": 2 } ], - \\"compatMode\\": \\"BackCompat\\", - \\"id\\": 1 + "compatMode": "BackCompat", + "id": 1 }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 + "initialOffset": { + "left": 0, + "top": 0 } } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [], - \\"adds\\": [ + "type": 3, + "data": { + "source": 0, + "texts": [], + "attributes": [], + "removes": [], + "adds": [ { - \\"parentId\\": 14, - \\"nextId\\": null, - \\"node\\": { - \\"id\\": 99, - \\"type\\": 2, - \\"tagName\\": \\"canvas\\", - \\"attributes\\": {}, - \\"childNodes\\": [] + "parentId": 14, + "nextId": null, + "node": { + "id": 99, + "type": 2, + "tagName": "canvas", + "attributes": {}, + "childNodes": [] } } ] @@ -302,32 +302,32 @@ exports[`pruneBranches should remove branches based on nodes that came in after `; exports[`pruneBranches should remove mutations that don't include ids 1`] = ` -Object { - "data": Object { - "adds": Array [ - Object { +{ + "data": { + "adds": [ + { "nextId": null, - "node": Object {}, + "node": {}, "parentId": 14, }, ], - "attributes": Array [ - Object { - "attributes": Object { + "attributes": [ + { + "attributes": { "data-attr": "Kept", }, "id": 14, }, ], - "removes": Array [ - Object { + "removes": [ + { "id": 15, "parentId": 14, }, ], "source": 0, - "texts": Array [ - Object { + "texts": [ + { "id": 15, "value": "Kept", }, @@ -341,22 +341,22 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" + 4 META charset="utf-8" + 5 RRText text=" \\n " 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" + 16 RRText text="\\n " + 17 DIV id="container" 34 DIV - 35 RRText text=\\"1\\" + 35 RRText text="1" 33 DIV - 36 RRText text=\\"2\\" + 36 RRText text="2" 32 DIV - 37 RRText text=\\"3\\" + 37 RRText text="3" 31 DIV - 38 RRText text=\\"4\\" + 38 RRText text="4" 29 DIV - 30 RRText text=\\"5\\" - 19 RRText text=\\"\\\\n \\\\n \\" + 30 RRText text="5" + 19 RRText text="\\n \\n " " `; @@ -364,22 +364,22 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" + 4 META charset="utf-8" + 5 RRText text=" \\n " 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" + 16 RRText text="\\n " + 17 DIV id="container" 29 DIV - 30 RRText text=\\"5\\" + 30 RRText text="5" 31 DIV - 38 RRText text=\\"4\\" + 38 RRText text="4" 32 DIV - 37 RRText text=\\"3\\" + 37 RRText text="3" 33 DIV - 36 RRText text=\\"2\\" + 36 RRText text="2" 34 DIV - 35 RRText text=\\"1\\" - 19 RRText text=\\"\\\\n \\\\n \\" + 35 RRText text="1" + 19 RRText text="\\n \\n " " `; @@ -387,12 +387,12 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" + 4 META charset="utf-8" + 5 RRText text=" \\n " 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 19 RRText text=\\"\\\\n \\\\n \\" + 16 RRText text="\\n " + 17 DIV id="container" + 19 RRText text="\\n \\n " " `; @@ -400,21 +400,21 @@ exports[`session cutter should cut events with inline styles: screenshot at 1000 "1 RRDocument 2 HTML 3 HEAD - 4 RRText text=\\"\\\\n \\" - 5 META charset=\\"utf-8\\" - 6 RRText text=\\"\\\\n \\" + 4 RRText text="\\n " + 5 META charset="utf-8" + 6 RRText text="\\n " 7 STYLE - -1 RRText text=\\"#root { background: yellow; width: 10px; height: 10px; }\\" - 8 RRText text=\\"\\\\n \\" + -1 RRText text="#root { background: yellow; width: 10px; height: 10px; }" + 8 RRText text="\\n " 9 STYLE - -1 RRText text=\\".block { width: 20px; height: 20px; background: red; }\\" - 10 RRText text=\\"\\\\n \\" - 11 RRText text=\\"\\\\n \\" + -1 RRText text=".block { width: 20px; height: 20px; background: red; }" + 10 RRText text="\\n " + 11 RRText text="\\n " 12 BODY - 13 RRText text=\\"\\\\n \\" - 14 DIV id=\\"root\\" - 15 RRText text=\\" \\\\n \\" - 16 RRText text=\\"\\\\n \\" - 17 DIV class=\\"block\\" + 13 RRText text="\\n " + 14 DIV id="root" + 15 RRText text=" \\n " + 16 RRText text="\\n " + 17 DIV class="block" " `; diff --git a/packages/rrweb-cutter/test/events/inline-style.event.ts b/packages/rrweb-cutter/test/events/inline-style.event.ts index 286d2d472c..bd024edff5 100644 --- a/packages/rrweb-cutter/test/events/inline-style.event.ts +++ b/packages/rrweb-cutter/test/events/inline-style.event.ts @@ -1,4 +1,4 @@ -import { EventType, eventWithTime, IncrementalSource } from 'rrweb/src/types'; +import { EventType, eventWithTime, IncrementalSource } from '@rrweb/types'; const now = Date.now(); diff --git a/packages/rrweb-cutter/test/events/mutation.event.ts b/packages/rrweb-cutter/test/events/mutation.event.ts index 2d1b84b8bd..62f9bf10ce 100644 --- a/packages/rrweb-cutter/test/events/mutation.event.ts +++ b/packages/rrweb-cutter/test/events/mutation.event.ts @@ -1,4 +1,4 @@ -import { EventType, eventWithTime, IncrementalSource } from 'rrweb/src/types'; +import { EventType, eventWithTime, IncrementalSource } from '@rrweb/types'; const now = Date.now(); diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index d2fa3617f8..be7f358e63 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -3,22 +3,16 @@ */ import path from 'path'; import fs from 'fs'; -import { - createMirror, - snapshot, - serializedNodeWithId, - NodeType, - elementNode, - documentNode, -} from 'rrweb-snapshot'; -import { EventType, SyncReplayer } from 'rrweb'; -import type { eventWithTime } from 'rrweb/typings/types'; +import { createMirror, snapshot, NodeType } from 'rrweb-snapshot'; +import { EventType } from 'rrweb'; +import { SyncReplayer } from 'rrweb/sync-replayer'; +import type { eventWithTime } from '@rrweb/types'; import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; -import { assertSnapshot } from './utils'; +import { assertSnapshot } from 'rrweb/test/utils'; describe('session cutter', () => { it('should return the same events if the events length is too short', () => { diff --git a/packages/rrweb-cutter/test/unit.test.ts b/packages/rrweb-cutter/test/unit.test.ts index 3f0dad3f8c..8c3dc58ce9 100644 --- a/packages/rrweb-cutter/test/unit.test.ts +++ b/packages/rrweb-cutter/test/unit.test.ts @@ -1,3 +1,6 @@ +/** + * @jest-environment jsdom + */ import { NodeType } from 'rrweb-snapshot'; import { getTreeForId } from '../src'; @@ -28,7 +31,6 @@ describe('getTreeForId', () => { }, [], ); - console.log(tree); expect(tree).toEqual(new Set([1, 2, 99])); }); it('should return parents id as part of tree 2', () => { @@ -57,7 +59,6 @@ describe('getTreeForId', () => { }, [], ); - console.log(tree); expect(tree).toEqual(new Set([1, 2])); }); @@ -100,7 +101,6 @@ describe('getTreeForId', () => { }, [], ); - console.log(tree); expect(tree).toEqual(new Set([1, 2, 98, 3, 99])); }); }); diff --git a/packages/rrweb-cutter/test/utils.ts b/packages/rrweb-cutter/test/utils.ts deleted file mode 100644 index a9a2d3568a..0000000000 --- a/packages/rrweb-cutter/test/utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { assertSnapshot as originalAssertSnapshot } from 'rrweb/test/utils'; -import { eventWithTime } from 'rrweb/typings/types'; -export function assertSnapshot(events: eventWithTime[]) { - /** - * for some reason there seems to be an inconsistency between - * import("rrweb/packages/rrweb/typings/types") - * and import("rrweb/packages/rrweb/src/types") - * which spawns the following error: - * Type 'incrementalSnapshotEvent & { timestamp: number; delay?: number | undefined; }' is not assignable to type 'incrementalSnapshotEvent'. - * @see https://ts-error-translator.vercel.app/?error=IIJw5grgtgpgdgFwAQHsBmSEE8AOMkDkAllDiiAgBQBEA9AKoDOMIjtAVhIwkXLSDDJsQIAO4wARrRwBDAMYBrGWBjCxk2thy8wbLauoBKAHQwAbvAQB1IggAWAFRIwA2gF0CSIoyRwUyGUZGIjA4GQkAG3wEFCRZEBlYBBZUDH1CEjIKGgZmVg4uHj4BIX51KVlFZVUy8SlGEDlNXAMTc0sbeydYdwJjACgkJAcWjNJyKjomFjZObl5+QRQ1Oul5JRUVjS0dPRbGI1MLRE7HZ09vX38kQODQ8KjMWPTiceypvNnChZLl2o1KhsaiJVg0mvoDm1jtZbGdYH1BkNhqNiHA5AJYIgZBEAMphHCMOz+ACi0KQADIkABvTDObiJHAALl80AkLAA3EgACYwCIyLAAfmZcFZKQAPkgIHAeWheDAuZyAL4XHx+AJBEJhSLRZ4o9onWHdGAIpFIkZ4MZZSa5GYFebFJZbCrrapO5raOC6d2tYy8dEwTEIbF4mQEokIUmWCnU2mwemkYWikCcnl8wWJqBskBICVSmVyhVIZVeVXXW6ah46zAozITHLTfJzIqLUoggEuzb-eqNb2Q31ojGWYP4wkksmUmk8ONBhMszMc7m8-lCudZnOS6UwWVweVKk2ms01gcBoe4kfhyOIaOTukzpmrhep5cZte5zfb3dFlVXdV3LWPGJqwtVF-UDYdQ1HCNoX3A9DzwHx0DiEAUDwCgsEILkZCDTwZAELw0RQUgsKIbUBlg2DzXwV4rXrT47WbX43UBV0u3dXZe0OP1ByxCIABEsJkb81RuDV7m1J4gKo2t3htRtvgdVtyjWKpOzbbtwX2TjjzAviBJg8jTUowhYC5IgZAASUQFh5B4FA4H47CSx-ES-0rCSXi4k8eIcwSyIM8ijOousPltJsfkdVjmNUpSdk9PZ4MOEyzMs5IEjkWz7L0pzhPLMSAN1YDpOtBsvntFs-jU5SgTdMEOJMJKLKstKMp8-T-IPQKGpS6z0qIOzWuystRP-KsXi6pqbL6uAAAVcMSNr2o6-ZUiQlCWGwQh9BwvC-UI2QeFIxFFv8wKito0L5PKpiO2BGLcHYiFDgAWXlZKJt6uzGCEobXPEwCPLeYq6LChSKqUqLbtBHtHpMF7TMa1LJs+vogA - * disabling the type check for now - */ - // @ts-ignore - originalAssertSnapshot(events); -} diff --git a/packages/rrweb-cutter/tsconfig.json b/packages/rrweb-cutter/tsconfig.json index 4c6ba4926a..55f4165e07 100644 --- a/packages/rrweb-cutter/tsconfig.json +++ b/packages/rrweb-cutter/tsconfig.json @@ -20,7 +20,8 @@ "skipLibCheck": true }, "compileOnSave": true, - "include": ["src", "../rrweb/src/record/workers/workers.d.ts"], + "include": ["src"], + "exclude": ["test"], "references": [ { "path": "../rrdom" diff --git a/packages/rrweb-cutter/vite.config.ts b/packages/rrweb-cutter/vite.config.ts index 048a3b541f..d7910ee321 100644 --- a/packages/rrweb-cutter/vite.config.ts +++ b/packages/rrweb-cutter/vite.config.ts @@ -7,7 +7,8 @@ export default { build: { lib: { entry: path.resolve(__dirname, 'src/index.ts'), - name: 'rrwebTypes', + name: 'rrwebCutter', + fileName: 'index', formats: ['es', 'cjs', 'umd', 'iife'], }, diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index f0b9bc4232..f21bc988e2 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -38,6 +38,23 @@ "es", "typings" ], + "exports": { + ".": { + "import": "./es/rrweb/packages/rrweb/src/entries/all.js", + "require": "./lib/rrweb-all.cjs" + }, + "./sync-replayer": { + "import": "./es/rrweb/packages/rrweb/src/replay/sync-replayer.js", + "require": "./lib/replay/rrweb-sync-replay.cjs" + } + }, + "typesVersions": { + "*": { + "sync-replayer": [ + "typings/replay/sync-replayer.d.ts" + ] + } + }, "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { diff --git a/yarn.lock b/yarn.lock index 18c892337a..e4e889c957 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz" @@ -16,6 +24,13 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.15.0": version "7.15.0" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz" @@ -26,6 +41,11 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== +"@babel/compat-data@^7.20.5": + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" + integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== + "@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0": version "7.15.5" resolved "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz" @@ -47,6 +67,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.11.6": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/core@^7.12.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz" @@ -86,6 +127,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.20.7": + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" + integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== + dependencies: + "@babel/types" "^7.20.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-compilation-targets@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz" @@ -106,6 +156,17 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" @@ -113,6 +174,11 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + "@babel/helper-function-name@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz" @@ -131,6 +197,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz" @@ -159,6 +233,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-member-expression-to-functions@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz" @@ -180,6 +261,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-module-transforms@^7.15.4": version "7.15.7" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz" @@ -208,6 +296,20 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" + "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz" @@ -220,6 +322,11 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.18.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + "@babel/helper-replace-supers@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz" @@ -244,6 +351,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + "@babel/helper-split-export-declaration@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz" @@ -258,6 +372,18 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + "@babel/helper-validator-identifier@^7.14.5": version "7.14.8" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz" @@ -273,6 +399,11 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz" @@ -283,6 +414,11 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + "@babel/helpers@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz" @@ -301,6 +437,15 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helpers@^7.20.7": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" + integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.13" + "@babel/types" "^7.20.7" + "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" @@ -319,6 +464,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.7.2": version "7.15.7" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz" @@ -329,6 +483,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz" integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA== +"@babel/parser@^7.20.13", "@babel/parser@^7.20.7": + version "7.20.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" + integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" @@ -364,6 +523,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" @@ -445,6 +611,15 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/template@^7.18.10", "@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@babel/traverse@^7.1.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.7.2": version "7.15.4" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz" @@ -476,6 +651,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.15.6" resolved "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz" @@ -492,6 +683,15 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -920,6 +1120,18 @@ jest-util "^27.5.1" slash "^3.0.0" +"@jest/console@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.2.tgz#f78374905c2454764152904a344a2d5226b0ef09" + integrity sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg== + dependencies: + "@jest/types" "^29.4.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + slash "^3.0.0" + "@jest/core@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/core/-/core-27.2.4.tgz" @@ -988,6 +1200,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.2.tgz#6e999b67bdc2df9d96ba9b142465bda71ee472c2" + integrity sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ== + dependencies: + "@jest/console" "^29.4.2" + "@jest/reporters" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.4.2" + jest-config "^29.4.2" + jest-haste-map "^29.4.2" + jest-message-util "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-resolve-dependencies "^29.4.2" + jest-runner "^29.4.2" + jest-runtime "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" + jest-watcher "^29.4.2" + micromatch "^4.0.4" + pretty-format "^29.4.2" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.2.4.tgz" @@ -1008,6 +1254,31 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/environment@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.2.tgz#ee92c316ee2fbdf0bcd9d2db0ef42d64fea26b56" + integrity sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ== + dependencies: + "@jest/fake-timers" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + jest-mock "^29.4.2" + +"@jest/expect-utils@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.2.tgz#cd0065dfdd8e8a182aa350cc121db97b5eed7b3f" + integrity sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA== + dependencies: + jest-get-type "^29.4.2" + +"@jest/expect@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.2.tgz#2d4a6a41b29380957c5094de19259f87f194578b" + integrity sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ== + dependencies: + expect "^29.4.2" + jest-snapshot "^29.4.2" + "@jest/fake-timers@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.2.4.tgz" @@ -1032,6 +1303,18 @@ jest-mock "^27.5.1" jest-util "^27.5.1" +"@jest/fake-timers@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.2.tgz#af43ee1a5720b987d0348f80df98f2cb17d45cd0" + integrity sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ== + dependencies: + "@jest/types" "^29.4.2" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.4.2" + jest-mock "^29.4.2" + jest-util "^29.4.2" + "@jest/globals@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.2.4.tgz" @@ -1050,6 +1333,16 @@ "@jest/types" "^27.5.1" expect "^27.5.1" +"@jest/globals@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.2.tgz#73f85f5db0e17642258b25fd0b9fc89ddedb50eb" + integrity sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/expect" "^29.4.2" + "@jest/types" "^29.4.2" + jest-mock "^29.4.2" + "@jest/reporters@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.2.4.tgz" @@ -1111,6 +1404,43 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/reporters@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.2.tgz#6abfa923941daae0acc76a18830ee9e79a22042d" + integrity sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" + "@jridgewell/trace-mapping" "^0.3.15" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + jest-worker "^29.4.2" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" + integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@jest/source-map@^27.0.6": version "27.0.6" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz" @@ -1129,6 +1459,15 @@ graceful-fs "^4.2.9" source-map "^0.6.0" +"@jest/source-map@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.2.tgz#f9815d59e25cd3d6828e41489cd239271018d153" + integrity sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.2.4.tgz" @@ -1149,6 +1488,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.2.tgz#34b0ba069f2e3072261e4884c8fb6bd15ed6fb8d" + integrity sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA== + dependencies: + "@jest/console" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.2.4.tgz" @@ -1169,6 +1518,16 @@ jest-haste-map "^27.5.1" jest-runtime "^27.5.1" +"@jest/test-sequencer@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz#8b48e5bc4af80b42edacaf2a733d4f295edf28fb" + integrity sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg== + dependencies: + "@jest/test-result" "^29.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.2" + slash "^3.0.0" + "@jest/transform@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.2.4.tgz" @@ -1211,6 +1570,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.2.tgz#b24b72dbab4c8675433a80e222d6a8ef4656fb81" + integrity sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.4.2" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.2" + jest-regex-util "^29.4.2" + jest-util "^29.4.2" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/types/-/types-27.2.4.tgz" @@ -1233,12 +1613,46 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jridgewell/resolve-uri@^3.0.3": +"@jest/types@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" + integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== + dependencies: + "@jest/schemas" "^29.4.2" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== @@ -1251,6 +1665,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz" @@ -2344,6 +2766,11 @@ colors "~1.2.1" string-argv "~0.3.1" +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -2351,6 +2778,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers@^8.0.1": version "8.0.1" resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz" @@ -2363,6 +2804,11 @@ resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@ts-morph/common@~0.18.0": version "0.18.1" resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" @@ -2490,6 +2936,13 @@ dependencies: "@types/node" "*" +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + "@types/inquirer@^8.2.1": version "8.2.1" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" @@ -2549,6 +3002,14 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/jest@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.4.0.tgz#a8444ad1704493e84dbf07bb05990b275b3b9206" + integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/jsdom@^20.0.0": version "20.0.0" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" @@ -2697,6 +3158,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.8": + version "17.0.22" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + dependencies: + "@types/yargs-parser" "*" + "@types/yauzl@^2.9.1": version "2.9.2" resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz" @@ -2937,6 +3405,11 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" @@ -2958,6 +3431,14 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" @@ -2968,7 +3449,7 @@ acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.1.1: +acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -2978,6 +3459,11 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.1.0, acorn@^8.8.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + acorn@^8.2.4: version "8.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz" @@ -3294,6 +3780,19 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.2.tgz#b17b9f64be288040877cbe2649f91ac3b63b2ba6" + integrity sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ== + dependencies: + "@jest/transform" "^29.4.2" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.4.2" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" @@ -3336,6 +3835,16 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz#22aa43e255230f02371ffef1cac7eedef58f60bc" + integrity sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" @@ -3370,6 +3879,14 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz#f0b20c6a79a9f155515e72a2d4f537fe002a4e38" + integrity sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ== + dependencies: + babel-plugin-jest-hoist "^29.4.2" + babel-preset-current-node-syntax "^1.0.0" + babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" @@ -3559,7 +4076,7 @@ browserslist@^4.17.5: node-releases "^2.0.1" picocolors "^1.0.0" -browserslist@^4.21.4, browserslist@^4.21.5: +browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -4248,6 +4765,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -4512,15 +5034,10 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": +cssom@^0.4.4, cssom@^0.5.0, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - cssom@~0.3.6: version "0.3.8" resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" @@ -4579,6 +5096,15 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" @@ -4638,6 +5164,11 @@ decimal.js@^10.2.1: resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decimal.js@^10.4.2: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -4758,6 +5289,11 @@ diff-sequences@^27.5.1: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda" + integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw== + diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" @@ -4816,6 +5352,13 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz" @@ -4902,6 +5445,11 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz#911d5df67542bf7554336142eb302c5ec90bba66" integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" @@ -4958,6 +5506,11 @@ entities@^4.3.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg== +entities@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -5576,6 +6129,17 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expect@^29.0.0, expect@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492" + integrity sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ== + dependencies: + "@jest/expect-utils" "^29.4.2" + jest-get-type "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + express@^4.16.4: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" @@ -5697,7 +6261,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -5912,6 +6476,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" @@ -6491,6 +7064,13 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -6542,6 +7122,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" @@ -6559,7 +7148,7 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@5.0.1: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -6591,7 +7180,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -7331,6 +7920,14 @@ jest-changed-files@^27.5.1: execa "^5.0.0" throat "^6.0.1" +jest-changed-files@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.2.tgz#bee1fafc8b620d6251423d1978a0080546bc4376" + integrity sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + jest-circus@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.2.4.tgz" @@ -7381,6 +7978,31 @@ jest-circus@^27.5.1: stack-utils "^2.0.3" throat "^6.0.1" +jest-circus@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.2.tgz#2d00c04baefd0ee2a277014cd494d4b5970663ed" + integrity sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/expect" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-runtime "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" + p-limit "^3.1.0" + pretty-format "^29.4.2" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.2.4.tgz" @@ -7417,6 +8039,24 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" +jest-cli@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.2.tgz#94a2f913a0a7a49d11bee98ad88bf48baae941f4" + integrity sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q== + dependencies: + "@jest/core" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" + prompts "^2.0.1" + yargs "^17.3.1" + jest-config@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.2.4.tgz" @@ -7474,6 +8114,34 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" +jest-config@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.2.tgz#15386dd9ed2f7059516915515f786b8836a98f07" + integrity sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.4.2" + "@jest/types" "^29.4.2" + babel-jest "^29.4.2" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.4.2" + jest-environment-node "^29.4.2" + jest-get-type "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-runner "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.4.2" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz" @@ -7504,6 +8172,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.2.tgz#b88502d5dc02d97f6512d73c37da8b36f49b4871" + integrity sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" + jest-docblock@^27.0.6: version "27.0.6" resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz" @@ -7518,6 +8196,13 @@ jest-docblock@^27.5.1: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.2.tgz#c78a95eedf9a24c0a6cc16cf2abdc4b8b0f2531b" + integrity sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag== + dependencies: + detect-newline "^3.0.0" + jest-each@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.2.4.tgz" @@ -7540,6 +8225,17 @@ jest-each@^27.5.1: jest-util "^27.5.1" pretty-format "^27.5.1" +jest-each@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.2.tgz#e1347aff1303f4c35470827a62c029d389c5d44a" + integrity sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA== + dependencies: + "@jest/types" "^29.4.2" + chalk "^4.0.0" + jest-get-type "^29.4.2" + jest-util "^29.4.2" + pretty-format "^29.4.2" + jest-environment-jsdom@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.2.4.tgz" @@ -7566,6 +8262,20 @@ jest-environment-jsdom@^27.5.1: jest-util "^27.5.1" jsdom "^16.6.0" +jest-environment-jsdom@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.4.2.tgz#0cf95ad846949280dd58bc91a9ca463b6b232dd8" + integrity sha512-v1sH4Q0JGM+LPEGqHNM+m+uTMf3vpXpKiuDYqWUAh+0c9+nc7scGE+qTR5JuE+OOTDnwfzPgcv9sMq6zWAOaVg== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/fake-timers" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.4.2" + jest-util "^29.4.2" + jsdom "^20.0.0" + jest-environment-node@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.2.4.tgz" @@ -7590,6 +8300,18 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" +jest-environment-node@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.2.tgz#0eab835b41e25fd0c1a72f62665fc8db08762ad2" + integrity sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/fake-timers" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + jest-mock "^29.4.2" + jest-util "^29.4.2" + jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz" @@ -7605,6 +8327,11 @@ jest-get-type@^27.5.1: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe" + integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg== + jest-haste-map@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.2.4.tgz" @@ -7645,6 +8372,25 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.2.tgz#9112df3f5121e643f1b2dcbaa86ab11b0b90b49a" + integrity sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw== + dependencies: + "@jest/types" "^29.4.2" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.2" + jest-util "^29.4.2" + jest-worker "^29.4.2" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-image-snapshot@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-5.2.0.tgz#4af046935b465f0460aa73e890717bbc25d431e9" @@ -7723,6 +8469,14 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-leak-detector@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz#8f05c6680e0cb46a1d577c0d3da9793bed3ea97b" + integrity sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA== + dependencies: + jest-get-type "^29.4.2" + pretty-format "^29.4.2" + jest-matcher-utils@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz" @@ -7752,6 +8506,16 @@ jest-matcher-utils@^27.2.4: jest-get-type "^27.0.6" pretty-format "^27.2.4" +jest-matcher-utils@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz#08d0bf5abf242e3834bec92c7ef5071732839e85" + integrity sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg== + dependencies: + chalk "^4.0.0" + jest-diff "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" + jest-message-util@^23.4.0: version "23.4.0" resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz" @@ -7793,6 +8557,21 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" + integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.4.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.4.2" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.2.4.tgz" @@ -7809,6 +8588,15 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" +jest-mock@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8" + integrity sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g== + dependencies: + "@jest/types" "^29.4.2" + "@types/node" "*" + jest-util "^29.4.2" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" @@ -7824,6 +8612,11 @@ jest-regex-util@^27.5.1: resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-regex-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.2.tgz#19187cca35d301f8126cf7a021dd4dcb7b58a1ca" + integrity sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig== + jest-resolve-dependencies@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.4.tgz" @@ -7842,6 +8635,14 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" +jest-resolve-dependencies@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz#6359db606f5967b68ca8bbe9dbc07a4306c12bf7" + integrity sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg== + dependencies: + jest-regex-util "^29.4.2" + jest-snapshot "^29.4.2" + jest-resolve@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz" @@ -7883,6 +8684,21 @@ jest-resolve@^27.5.1: resolve.exports "^1.1.0" slash "^3.0.0" +jest-resolve@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.2.tgz#8831f449671d08d161fe493003f61dc9b55b808e" + integrity sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.2" + jest-pnp-resolver "^1.2.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.2.4.tgz" @@ -7938,6 +8754,33 @@ jest-runner@^27.5.1: source-map-support "^0.5.6" throat "^6.0.1" +jest-runner@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.2.tgz#2bcecf72303369df4ef1e6e983c22a89870d5125" + integrity sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg== + dependencies: + "@jest/console" "^29.4.2" + "@jest/environment" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.2" + jest-environment-node "^29.4.2" + jest-haste-map "^29.4.2" + jest-leak-detector "^29.4.2" + jest-message-util "^29.4.2" + jest-resolve "^29.4.2" + jest-runtime "^29.4.2" + jest-util "^29.4.2" + jest-watcher "^29.4.2" + jest-worker "^29.4.2" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.2.4.tgz" @@ -7999,6 +8842,35 @@ jest-runtime@^27.5.1: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.2.tgz#d86b764c5b95d76cb26ed1f32644e99de5d5c134" + integrity sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/fake-timers" "^29.4.2" + "@jest/globals" "^29.4.2" + "@jest/source-map" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.2" + jest-message-util "^29.4.2" + jest-mock "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" + semver "^7.3.5" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-serializer@^27.0.6: version "27.0.6" resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz" @@ -8089,6 +8961,36 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" +jest-snapshot@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.2.tgz#ba1fb9abb279fd2c85109ff1757bc56b503bbb3a" + integrity sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.4.2" + graceful-fs "^4.2.9" + jest-diff "^29.4.2" + jest-get-type "^29.4.2" + jest-haste-map "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + natural-compare "^1.4.0" + pretty-format "^29.4.2" + semver "^7.3.5" + jest-util@^27.0.0, jest-util@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.2.4.tgz" @@ -8113,6 +9015,18 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" + integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== + dependencies: + "@jest/types" "^29.4.2" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.2.4.tgz" @@ -8137,6 +9051,18 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" +jest-validate@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.2.tgz#3b3f8c4910ab9a3442d2512e2175df6b3f77b915" + integrity sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q== + dependencies: + "@jest/types" "^29.4.2" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.2" + leven "^3.1.0" + pretty-format "^29.4.2" + jest-watcher@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.2.4.tgz" @@ -8163,6 +9089,20 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" +jest-watcher@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.2.tgz#09c0f4c9a9c7c0807fcefb1445b821c6f7953b7c" + integrity sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w== + dependencies: + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.4.2" + string-length "^4.0.1" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" @@ -8190,6 +9130,26 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.2.tgz#d9b2c3bafc69311d84d94e7fb45677fc8976296f" + integrity sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw== + dependencies: + "@types/node" "*" + jest-util "^29.4.2" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.2.tgz#4c2127d03a71dc187f386156ef155dbf323fb7be" + integrity sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg== + dependencies: + "@jest/core" "^29.4.2" + "@jest/types" "^29.4.2" + import-local "^3.0.2" + jest-cli "^29.4.2" + jest@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest/-/jest-27.2.4.tgz" @@ -8309,6 +9269,38 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -8358,6 +9350,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonc-parser@^3.0.0, jsonc-parser@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz" @@ -8661,6 +9658,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -8738,6 +9742,13 @@ make-fetch-happen@^9.0.1: socks-proxy-agent "^5.0.0" ssri "^8.0.0" +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" @@ -9458,6 +10469,11 @@ nwsapi@^2.2.0: resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nwsapi@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" @@ -9657,7 +10673,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -9843,6 +10859,13 @@ parse5@^7.0.0: dependencies: entities "^4.3.0" +parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -10459,6 +11482,15 @@ pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" + integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== + dependencies: + "@jest/schemas" "^29.4.2" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -10638,6 +11670,11 @@ query-string@^6.13.8: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -10684,6 +11721,11 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" @@ -10927,6 +11969,11 @@ require-relative@^0.8.7: resolved "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz" integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -10954,6 +12001,11 @@ resolve.exports@^1.1.0: resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" + integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== + resolve@1.1.7: version "1.1.7" resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" @@ -11248,6 +12300,13 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + semver-match@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/semver-match/-/semver-match-0.1.1.tgz#e7ccb31f83fd4a0e377d66387afd8ca3a329b5fc" @@ -11377,6 +12436,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-peer-light@^9.10.0: version "9.10.0" resolved "https://registry.yarnpkg.com/simple-peer-light/-/simple-peer-light-9.10.0.tgz#6f3699b80e4b6d3a9374a865e1a8e497aa623afb" @@ -11483,6 +12547,14 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" @@ -12077,7 +13149,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.x: +tmpl@1.0.5, tmpl@1.0.x: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== @@ -12113,6 +13185,16 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" @@ -12128,6 +13210,13 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -12183,6 +13272,20 @@ ts-jest@^27.1.3: semver "7.x" yargs-parser "20.x" +ts-jest@^29.0.5: + version "29.0.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" + integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + ts-morph@17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" @@ -12540,6 +13643,11 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" @@ -12584,6 +13692,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -12646,6 +13762,15 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +v8-to-istanbul@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" + integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" @@ -12725,6 +13850,13 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + walker@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" @@ -12732,6 +13864,13 @@ walker@^1.0.7: dependencies: makeerror "1.0.x" +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" @@ -12754,6 +13893,11 @@ webidl-conversions@^6.1.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" @@ -12761,11 +13905,31 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" @@ -12901,6 +14065,14 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz" @@ -12959,11 +14131,21 @@ ws@^7.4.6: resolved "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +ws@^8.11.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -12989,7 +14171,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== -yallist@^3.0.0, yallist@^3.1.1: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -13022,7 +14204,7 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^21.1.1: +yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -13057,7 +14239,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.1.1: +yargs@^17.1.1, yargs@^17.3.1: version "17.6.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== From c185364c43e3687a8c83090a533a267926b679e0 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 00:57:20 +1100 Subject: [PATCH 23/52] fix failed tests --- packages/rrweb-cutter/tsconfig.json | 16 +++++++++++++--- packages/rrweb/test/events/node-mutation.ts | 2 +- packages/rrweb/test/replay/sync-replayer.test.ts | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/rrweb-cutter/tsconfig.json b/packages/rrweb-cutter/tsconfig.json index 55f4165e07..8a26bf918e 100644 --- a/packages/rrweb-cutter/tsconfig.json +++ b/packages/rrweb-cutter/tsconfig.json @@ -6,7 +6,10 @@ "declarationDir": "dist/types", "target": "ES5", "module": "commonjs", - "lib": ["ES6", "DOM"], + "lib": [ + "ES6", + "DOM" + ], "moduleResolution": "Node", "sourceMap": true, "strictNullChecks": true, @@ -20,9 +23,16 @@ "skipLibCheck": true }, "compileOnSave": true, - "include": ["src"], - "exclude": ["test"], + "include": [ + "src" + ], + "exclude": [ + "test" + ], "references": [ + { + "path": "../types" + }, { "path": "../rrdom" }, diff --git a/packages/rrweb/test/events/node-mutation.ts b/packages/rrweb/test/events/node-mutation.ts index 98a13e3b8b..62f9bf10ce 100644 --- a/packages/rrweb/test/events/node-mutation.ts +++ b/packages/rrweb/test/events/node-mutation.ts @@ -1,4 +1,4 @@ -import { EventType, eventWithTime, IncrementalSource } from '../../src/types'; +import { EventType, eventWithTime, IncrementalSource } from '@rrweb/types'; const now = Date.now(); diff --git a/packages/rrweb/test/replay/sync-replayer.test.ts b/packages/rrweb/test/replay/sync-replayer.test.ts index 900978557e..975aeb8999 100644 --- a/packages/rrweb/test/replay/sync-replayer.test.ts +++ b/packages/rrweb/test/replay/sync-replayer.test.ts @@ -1,6 +1,6 @@ import { printRRDom } from 'rrdom'; import { SyncReplayer } from '../../src/replay/sync-replayer'; -import { EventType, eventWithTime } from '../../src/types'; +import { EventType, eventWithTime } from '@rrweb/types'; import { events as mutationEvents } from '../events/node-mutation'; describe('A synchronous replayer purely built with RRDom', () => { From efdcbe8e5f8d6235d77237a7d074c0105e4c5505 Mon Sep 17 00:00:00 2001 From: YunFeng0817 Date: Mon, 13 Feb 2023 13:58:53 +0000 Subject: [PATCH 24/52] Apply formatting changes --- yarn.lock | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index e4e889c957..8f79095803 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5034,10 +5034,15 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4, cssom@^0.5.0, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": +cssom@^0.4.4, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + cssom@~0.3.6: version "0.3.8" resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" From a490b2dc7fb535ccf9c7f1059d12a66bfc7a5286 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 01:28:33 +1100 Subject: [PATCH 25/52] export SyncReplayer to entry/all.ts --- packages/rrweb-cutter/src/index.ts | 2 +- .../rrweb-cutter/test/session-cutter.test.ts | 2 +- packages/rrweb/package.json | 17 ----------------- packages/rrweb/src/entries/all.ts | 1 + 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index e3fdc7909d..f62cd5f2d7 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -5,7 +5,7 @@ import type { } from '@rrweb/types'; import { IncrementalSource } from 'rrweb'; import { EventType } from 'rrweb'; -import { SyncReplayer } from 'rrweb/sync-replayer'; +import { SyncReplayer } from 'rrweb'; import snapshot from './snapshot'; import { serializedNodeWithId } from 'rrweb-snapshot'; type CutterConfig = { diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/session-cutter.test.ts index be7f358e63..7cb6ad84d2 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/session-cutter.test.ts @@ -5,7 +5,7 @@ import path from 'path'; import fs from 'fs'; import { createMirror, snapshot, NodeType } from 'rrweb-snapshot'; import { EventType } from 'rrweb'; -import { SyncReplayer } from 'rrweb/sync-replayer'; +import { SyncReplayer } from 'rrweb'; import type { eventWithTime } from '@rrweb/types'; import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index f21bc988e2..f0b9bc4232 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -38,23 +38,6 @@ "es", "typings" ], - "exports": { - ".": { - "import": "./es/rrweb/packages/rrweb/src/entries/all.js", - "require": "./lib/rrweb-all.cjs" - }, - "./sync-replayer": { - "import": "./es/rrweb/packages/rrweb/src/replay/sync-replayer.js", - "require": "./lib/replay/rrweb-sync-replay.cjs" - } - }, - "typesVersions": { - "*": { - "sync-replayer": [ - "typings/replay/sync-replayer.d.ts" - ] - } - }, "author": "yanzhen@smartx.com", "license": "MIT", "bugs": { diff --git a/packages/rrweb/src/entries/all.ts b/packages/rrweb/src/entries/all.ts index d67ff92447..7be9c683f6 100644 --- a/packages/rrweb/src/entries/all.ts +++ b/packages/rrweb/src/entries/all.ts @@ -1,4 +1,5 @@ export * from '../index'; +export { SyncReplayer } from '../replay/sync-replayer'; export * from '../packer'; export * from '../plugins/console/record'; export * from '../plugins/console/replay'; From 0d8d0c665f3bcc4da22ccf362386d0b24b41e270 Mon Sep 17 00:00:00 2001 From: YunFeng0817 Date: Mon, 13 Feb 2023 14:30:07 +0000 Subject: [PATCH 26/52] Apply formatting changes --- yarn.lock | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8f79095803..e4e889c957 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5034,15 +5034,10 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": +cssom@^0.4.4, cssom@^0.5.0, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - cssom@~0.3.6: version "0.3.8" resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" From 329718859216104826672f1504833b5888f4fe6e Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 01:39:57 +1100 Subject: [PATCH 27/52] downgrade jest --- packages/rrweb-cutter/package.json | 7 +- .../__snapshots__/session-cutter.test.ts.snap | 446 +++--- yarn.lock | 1218 +---------------- 3 files changed, 244 insertions(+), 1427 deletions(-) diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index e70cd7322f..f1ade15db5 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -34,10 +34,9 @@ "dist" ], "devDependencies": { - "@types/jest": "^29.4.0", - "jest": "29.4.2", - "jest-environment-jsdom": "^29.4.2", - "ts-jest": "^29.0.5", + "@types/jest": "^27.4.1", + "jest": "^27.5.1", + "ts-jest": "^27.1.3", "typescript": "^4.7.3", "vite": "^4.1.1", "vite-plugin-dts": "^1.7.2" diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap index a8f9628df9..d113052141 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap @@ -5,120 +5,120 @@ exports[`pruneBranches should cut branches that doesn't include id: pruned all b -1 RRDocumentType 2 HTML 12 BODY - 14 DIV id="root" - 15 RRText text=" \\n " + 14 DIV id=\\"root\\" + 15 RRText text=\\" \\\\n \\" " `; exports[`pruneBranches should keep branches where target child nodes was, and gets moved to 1`] = ` "[ { - "type": 0, - "data": {} + \\"type\\": 0, + \\"data\\": {} }, { - "type": 1, - "data": {} + \\"type\\": 1, + \\"data\\": {} }, { - "type": 4, - "data": { - "href": "about:blank", - "width": 1512, - "height": 395 + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 } }, { - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"utf-8\\" }, - "childNodes": [], - "id": 5 + \\"childNodes\\": [], + \\"id\\": 5 } ], - "id": 3 + \\"id\\": 3 }, { - "type": 2, - "tagName": "body", - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ { - "type": 2, - "tagName": "div", - "attributes": { - "id": "root" + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" }, - "childNodes": [ + \\"childNodes\\": [ { - "type": 3, - "textContent": " \\n ", - "id": 15 + \\"type\\": 3, + \\"textContent\\": \\" \\\\n \\", + \\"id\\": 15 } ], - "id": 14 + \\"id\\": 14 } ], - "id": 12 + \\"id\\": 12 } ], - "id": 2 + \\"id\\": 2 } ], - "compatMode": "BackCompat", - "id": 1 + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 }, - "initialOffset": { - "left": 0, - "top": 0 + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 } } }, { - "type": 3, - "data": { - "source": 2, - "type": 2, - "id": 15 + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 15 } }, { - "type": 3, - "data": { - "source": 0, - "texts": [], - "attributes": [], - "removes": [ + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ { - "parentId": 14, - "id": 15 + \\"parentId\\": 14, + \\"id\\": 15 } ], - "adds": [ + \\"adds\\": [ { - "parentId": 5, - "nextId": null, - "node": { - "type": 3, - "textContent": " \\n ", - "id": 15 + \\"parentId\\": 5, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\" \\\\n \\", + \\"id\\": 15 } } ] @@ -130,84 +130,84 @@ exports[`pruneBranches should keep branches where target child nodes was, and ge exports[`pruneBranches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` "[ { - "type": 0, - "data": {} + \\"type\\": 0, + \\"data\\": {} }, { - "type": 1, - "data": {} + \\"type\\": 1, + \\"data\\": {} }, { - "type": 4, - "data": { - "href": "about:blank", - "width": 1512, - "height": 395 + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 } }, { - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ { - "type": 2, - "tagName": "body", - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ { - "type": 2, - "tagName": "div", - "attributes": { - "id": "root" + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" }, - "childNodes": [], - "id": 14 + \\"childNodes\\": [], + \\"id\\": 14 } ], - "id": 12 + \\"id\\": 12 } ], - "id": 2 + \\"id\\": 2 } ], - "compatMode": "BackCompat", - "id": 1 + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 }, - "initialOffset": { - "left": 0, - "top": 0 + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 } } }, { - "type": 3, - "data": { - "source": 0, - "texts": [], - "attributes": [], - "removes": [], - "adds": [ + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ { - "parentId": 14, - "nextId": null, - "node": { - "id": 98, - "type": 2, - "tagName": "main", - "attributes": {}, - "childNodes": [ + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"id\\": 98, + \\"type\\": 2, + \\"tagName\\": \\"main\\", + \\"attributes\\": {}, + \\"childNodes\\": [ { - "id": 99, - "type": 2, - "tagName": "canvas", - "attributes": {}, - "childNodes": [] + \\"id\\": 99, + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [] } ] } @@ -221,78 +221,78 @@ exports[`pruneBranches should remove branches based on child nodes that came in exports[`pruneBranches should remove branches based on nodes that came in after fullsnapshot 1`] = ` "[ { - "type": 0, - "data": {} + \\"type\\": 0, + \\"data\\": {} }, { - "type": 1, - "data": {} + \\"type\\": 1, + \\"data\\": {} }, { - "type": 4, - "data": { - "href": "about:blank", - "width": 1512, - "height": 395 + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1512, + \\"height\\": 395 } }, { - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ { - "type": 2, - "tagName": "body", - "childNodes": [ + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"childNodes\\": [ { - "type": 2, - "tagName": "div", - "attributes": { - "id": "root" + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"root\\" }, - "childNodes": [], - "id": 14 + \\"childNodes\\": [], + \\"id\\": 14 } ], - "id": 12 + \\"id\\": 12 } ], - "id": 2 + \\"id\\": 2 } ], - "compatMode": "BackCompat", - "id": 1 + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 }, - "initialOffset": { - "left": 0, - "top": 0 + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 } } }, { - "type": 3, - "data": { - "source": 0, - "texts": [], - "attributes": [], - "removes": [], - "adds": [ + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ { - "parentId": 14, - "nextId": null, - "node": { - "id": 99, - "type": 2, - "tagName": "canvas", - "attributes": {}, - "childNodes": [] + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"id\\": 99, + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [] } } ] @@ -302,32 +302,32 @@ exports[`pruneBranches should remove branches based on nodes that came in after `; exports[`pruneBranches should remove mutations that don't include ids 1`] = ` -{ - "data": { - "adds": [ - { +Object { + "data": Object { + "adds": Array [ + Object { "nextId": null, - "node": {}, + "node": Object {}, "parentId": 14, }, ], - "attributes": [ - { - "attributes": { + "attributes": Array [ + Object { + "attributes": Object { "data-attr": "Kept", }, "id": 14, }, ], - "removes": [ - { + "removes": Array [ + Object { "id": 15, "parentId": 14, }, ], "source": 0, - "texts": [ - { + "texts": Array [ + Object { "id": 15, "value": "Kept", }, @@ -341,22 +341,22 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset="utf-8" - 5 RRText text=" \\n " + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" 6 BODY - 16 RRText text="\\n " - 17 DIV id="container" + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" 34 DIV - 35 RRText text="1" + 35 RRText text=\\"1\\" 33 DIV - 36 RRText text="2" + 36 RRText text=\\"2\\" 32 DIV - 37 RRText text="3" + 37 RRText text=\\"3\\" 31 DIV - 38 RRText text="4" + 38 RRText text=\\"4\\" 29 DIV - 30 RRText text="5" - 19 RRText text="\\n \\n " + 30 RRText text=\\"5\\" + 19 RRText text=\\"\\\\n \\\\n \\" " `; @@ -364,22 +364,22 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset="utf-8" - 5 RRText text=" \\n " + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" 6 BODY - 16 RRText text="\\n " - 17 DIV id="container" + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" 29 DIV - 30 RRText text="5" + 30 RRText text=\\"5\\" 31 DIV - 38 RRText text="4" + 38 RRText text=\\"4\\" 32 DIV - 37 RRText text="3" + 37 RRText text=\\"3\\" 33 DIV - 36 RRText text="2" + 36 RRText text=\\"2\\" 34 DIV - 35 RRText text="1" - 19 RRText text="\\n \\n " + 35 RRText text=\\"1\\" + 19 RRText text=\\"\\\\n \\\\n \\" " `; @@ -387,12 +387,12 @@ exports[`session cutter Cut the session events from several time points should c "1 RRDocument 2 HTML 3 HEAD - 4 META charset="utf-8" - 5 RRText text=" \\n " + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" 6 BODY - 16 RRText text="\\n " - 17 DIV id="container" - 19 RRText text="\\n \\n " + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 19 RRText text=\\"\\\\n \\\\n \\" " `; @@ -400,21 +400,21 @@ exports[`session cutter should cut events with inline styles: screenshot at 1000 "1 RRDocument 2 HTML 3 HEAD - 4 RRText text="\\n " - 5 META charset="utf-8" - 6 RRText text="\\n " + 4 RRText text=\\"\\\\n \\" + 5 META charset=\\"utf-8\\" + 6 RRText text=\\"\\\\n \\" 7 STYLE - -1 RRText text="#root { background: yellow; width: 10px; height: 10px; }" - 8 RRText text="\\n " + -1 RRText text=\\"#root { background: yellow; width: 10px; height: 10px; }\\" + 8 RRText text=\\"\\\\n \\" 9 STYLE - -1 RRText text=".block { width: 20px; height: 20px; background: red; }" - 10 RRText text="\\n " - 11 RRText text="\\n " + -1 RRText text=\\".block { width: 20px; height: 20px; background: red; }\\" + 10 RRText text=\\"\\\\n \\" + 11 RRText text=\\"\\\\n \\" 12 BODY - 13 RRText text="\\n " - 14 DIV id="root" - 15 RRText text=" \\n " - 16 RRText text="\\n " - 17 DIV class="block" + 13 RRText text=\\"\\\\n \\" + 14 DIV id=\\"root\\" + 15 RRText text=\\" \\\\n \\" + 16 RRText text=\\"\\\\n \\" + 17 DIV class=\\"block\\" " `; diff --git a/yarn.lock b/yarn.lock index e4e889c957..335e74ebf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,6 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz" @@ -24,13 +16,6 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - "@babel/compat-data@^7.15.0": version "7.15.0" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz" @@ -41,11 +26,6 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== -"@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== - "@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0": version "7.15.5" resolved "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz" @@ -67,27 +47,6 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.11.6": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - "@babel/core@^7.12.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz" @@ -127,15 +86,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.20.7": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== - dependencies: - "@babel/types" "^7.20.7" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - "@babel/helper-compilation-targets@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz" @@ -156,17 +106,6 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" - "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" @@ -174,11 +113,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - "@babel/helper-function-name@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz" @@ -197,14 +131,6 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz" @@ -233,13 +159,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-member-expression-to-functions@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz" @@ -261,13 +180,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-module-transforms@^7.15.4": version "7.15.7" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz" @@ -296,20 +208,6 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" - "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz" @@ -322,11 +220,6 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.18.6": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - "@babel/helper-replace-supers@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz" @@ -351,13 +244,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - "@babel/helper-split-export-declaration@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz" @@ -372,18 +258,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - "@babel/helper-validator-identifier@^7.14.5": version "7.14.8" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz" @@ -399,11 +273,6 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz" @@ -414,11 +283,6 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - "@babel/helpers@^7.15.4": version "7.15.4" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz" @@ -437,15 +301,6 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helpers@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" - "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" @@ -464,15 +319,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/parser@^7.1.0", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.7.2": version "7.15.7" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz" @@ -483,11 +329,6 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz" integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA== -"@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" @@ -523,13 +364,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" @@ -611,15 +445,6 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@babel/traverse@^7.1.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.7.2": version "7.15.4" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz" @@ -651,22 +476,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" - debug "^4.1.0" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.15.6" resolved "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz" @@ -683,15 +492,6 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -1120,18 +920,6 @@ jest-util "^27.5.1" slash "^3.0.0" -"@jest/console@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.2.tgz#f78374905c2454764152904a344a2d5226b0ef09" - integrity sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg== - dependencies: - "@jest/types" "^29.4.2" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.4.2" - jest-util "^29.4.2" - slash "^3.0.0" - "@jest/core@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/core/-/core-27.2.4.tgz" @@ -1200,40 +988,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.2.tgz#6e999b67bdc2df9d96ba9b142465bda71ee472c2" - integrity sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ== - dependencies: - "@jest/console" "^29.4.2" - "@jest/reporters" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.4.2" - jest-config "^29.4.2" - jest-haste-map "^29.4.2" - jest-message-util "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-resolve-dependencies "^29.4.2" - jest-runner "^29.4.2" - jest-runtime "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" - jest-watcher "^29.4.2" - micromatch "^4.0.4" - pretty-format "^29.4.2" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/environment@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.2.4.tgz" @@ -1254,31 +1008,6 @@ "@types/node" "*" jest-mock "^27.5.1" -"@jest/environment@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.2.tgz#ee92c316ee2fbdf0bcd9d2db0ef42d64fea26b56" - integrity sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ== - dependencies: - "@jest/fake-timers" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - jest-mock "^29.4.2" - -"@jest/expect-utils@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.2.tgz#cd0065dfdd8e8a182aa350cc121db97b5eed7b3f" - integrity sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA== - dependencies: - jest-get-type "^29.4.2" - -"@jest/expect@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.2.tgz#2d4a6a41b29380957c5094de19259f87f194578b" - integrity sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ== - dependencies: - expect "^29.4.2" - jest-snapshot "^29.4.2" - "@jest/fake-timers@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.2.4.tgz" @@ -1303,18 +1032,6 @@ jest-mock "^27.5.1" jest-util "^27.5.1" -"@jest/fake-timers@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.2.tgz#af43ee1a5720b987d0348f80df98f2cb17d45cd0" - integrity sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ== - dependencies: - "@jest/types" "^29.4.2" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.4.2" - jest-mock "^29.4.2" - jest-util "^29.4.2" - "@jest/globals@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.2.4.tgz" @@ -1333,16 +1050,6 @@ "@jest/types" "^27.5.1" expect "^27.5.1" -"@jest/globals@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.2.tgz#73f85f5db0e17642258b25fd0b9fc89ddedb50eb" - integrity sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/expect" "^29.4.2" - "@jest/types" "^29.4.2" - jest-mock "^29.4.2" - "@jest/reporters@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.2.4.tgz" @@ -1404,43 +1111,6 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/reporters@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.2.tgz#6abfa923941daae0acc76a18830ee9e79a22042d" - integrity sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" - "@jridgewell/trace-mapping" "^0.3.15" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.4.2" - jest-util "^29.4.2" - jest-worker "^29.4.2" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" - integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== - dependencies: - "@sinclair/typebox" "^0.25.16" - "@jest/source-map@^27.0.6": version "27.0.6" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz" @@ -1459,15 +1129,6 @@ graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/source-map@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.2.tgz#f9815d59e25cd3d6828e41489cd239271018d153" - integrity sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q== - dependencies: - "@jridgewell/trace-mapping" "^0.3.15" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/test-result@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.2.4.tgz" @@ -1488,16 +1149,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.2.tgz#34b0ba069f2e3072261e4884c8fb6bd15ed6fb8d" - integrity sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA== - dependencies: - "@jest/console" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-sequencer@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.2.4.tgz" @@ -1518,16 +1169,6 @@ jest-haste-map "^27.5.1" jest-runtime "^27.5.1" -"@jest/test-sequencer@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz#8b48e5bc4af80b42edacaf2a733d4f295edf28fb" - integrity sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg== - dependencies: - "@jest/test-result" "^29.4.2" - graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - slash "^3.0.0" - "@jest/transform@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.2.4.tgz" @@ -1570,27 +1211,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.2.tgz#b24b72dbab4c8675433a80e222d6a8ef4656fb81" - integrity sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.4.2" - "@jridgewell/trace-mapping" "^0.3.15" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - jest-regex-util "^29.4.2" - jest-util "^29.4.2" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.2" - "@jest/types@^27.2.4": version "27.2.4" resolved "https://registry.npmjs.org/@jest/types/-/types-27.2.4.tgz" @@ -1613,46 +1233,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" - integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== - dependencies: - "@jest/schemas" "^29.4.2" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== @@ -1665,14 +1251,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz" @@ -2766,11 +2344,6 @@ colors "~1.2.1" string-argv "~0.3.1" -"@sinclair/typebox@^0.25.16": - version "0.25.21" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" - integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== - "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -2778,20 +2351,6 @@ dependencies: type-detect "4.0.8" -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" - integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== - dependencies: - "@sinonjs/commons" "^2.0.0" - "@sinonjs/fake-timers@^8.0.1": version "8.0.1" resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz" @@ -2804,11 +2363,6 @@ resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - "@ts-morph/common@~0.18.0": version "0.18.1" resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" @@ -2936,13 +2490,6 @@ dependencies: "@types/node" "*" -"@types/graceful-fs@^4.1.3": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" - integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== - dependencies: - "@types/node" "*" - "@types/inquirer@^8.2.1": version "8.2.1" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" @@ -3002,14 +2549,6 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/jest@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.4.0.tgz#a8444ad1704493e84dbf07bb05990b275b3b9206" - integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - "@types/jsdom@^20.0.0": version "20.0.0" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" @@ -3158,13 +2697,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== - dependencies: - "@types/yargs-parser" "*" - "@types/yauzl@^2.9.1": version "2.9.2" resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz" @@ -3405,11 +2937,6 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" @@ -3431,14 +2958,6 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-globals@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" - integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== - dependencies: - acorn "^8.1.0" - acorn-walk "^8.0.2" - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" @@ -3449,7 +2968,7 @@ acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.2, acorn-walk@^8.1.1: +acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -3459,11 +2978,6 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.1.0, acorn@^8.8.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - acorn@^8.2.4: version "8.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz" @@ -3780,19 +3294,6 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" -babel-jest@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.2.tgz#b17b9f64be288040877cbe2649f91ac3b63b2ba6" - integrity sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ== - dependencies: - "@jest/transform" "^29.4.2" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.4.2" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" @@ -3835,16 +3336,6 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz#22aa43e255230f02371ffef1cac7eedef58f60bc" - integrity sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" @@ -3879,14 +3370,6 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz#f0b20c6a79a9f155515e72a2d4f537fe002a4e38" - integrity sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ== - dependencies: - babel-plugin-jest-hoist "^29.4.2" - babel-preset-current-node-syntax "^1.0.0" - babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" @@ -4076,7 +3559,7 @@ browserslist@^4.17.5: node-releases "^2.0.1" picocolors "^1.0.0" -browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: +browserslist@^4.21.4, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -4765,11 +4248,6 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -5034,10 +4512,15 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4, cssom@^0.5.0, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": +cssom@^0.4.4, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + cssom@~0.3.6: version "0.3.8" resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" @@ -5096,15 +4579,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -data-urls@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" - integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== - dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" @@ -5164,11 +4638,6 @@ decimal.js@^10.2.1: resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== -decimal.js@^10.4.2: - version "10.4.3" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" - integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== - decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -5289,11 +4758,6 @@ diff-sequences@^27.5.1: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== -diff-sequences@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda" - integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw== - diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" @@ -5352,13 +4816,6 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domexception@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" - integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== - dependencies: - webidl-conversions "^7.0.0" - domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz" @@ -5445,11 +4902,6 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz#911d5df67542bf7554336142eb302c5ec90bba66" integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - emittery@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" @@ -5506,11 +4958,6 @@ entities@^4.3.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg== -entities@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" - integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== - entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -6129,17 +5576,6 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" -expect@^29.0.0, expect@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492" - integrity sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ== - dependencies: - "@jest/expect-utils" "^29.4.2" - jest-get-type "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-util "^29.4.2" - express@^4.16.4: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" @@ -6261,7 +5697,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -6476,15 +5912,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" @@ -7064,13 +6491,6 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-encoding-sniffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" - integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== - dependencies: - whatwg-encoding "^2.0.0" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -7122,15 +6542,6 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" @@ -7148,7 +6559,7 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.1: +https-proxy-agent@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -7180,7 +6591,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.2: +iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -7920,14 +7331,6 @@ jest-changed-files@^27.5.1: execa "^5.0.0" throat "^6.0.1" -jest-changed-files@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.2.tgz#bee1fafc8b620d6251423d1978a0080546bc4376" - integrity sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - jest-circus@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.2.4.tgz" @@ -7978,31 +7381,6 @@ jest-circus@^27.5.1: stack-utils "^2.0.3" throat "^6.0.1" -jest-circus@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.2.tgz#2d00c04baefd0ee2a277014cd494d4b5970663ed" - integrity sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/expect" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-runtime "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" - p-limit "^3.1.0" - pretty-format "^29.4.2" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-cli@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.2.4.tgz" @@ -8039,24 +7417,6 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" -jest-cli@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.2.tgz#94a2f913a0a7a49d11bee98ad88bf48baae941f4" - integrity sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q== - dependencies: - "@jest/core" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" - prompts "^2.0.1" - yargs "^17.3.1" - jest-config@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.2.4.tgz" @@ -8114,34 +7474,6 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-config@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.2.tgz#15386dd9ed2f7059516915515f786b8836a98f07" - integrity sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.4.2" - "@jest/types" "^29.4.2" - babel-jest "^29.4.2" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.4.2" - jest-environment-node "^29.4.2" - jest-get-type "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-runner "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.4.2" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-diff@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz" @@ -8172,16 +7504,6 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-diff@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.2.tgz#b88502d5dc02d97f6512d73c37da8b36f49b4871" - integrity sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.4.2" - jest-get-type "^29.4.2" - pretty-format "^29.4.2" - jest-docblock@^27.0.6: version "27.0.6" resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz" @@ -8196,13 +7518,6 @@ jest-docblock@^27.5.1: dependencies: detect-newline "^3.0.0" -jest-docblock@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.2.tgz#c78a95eedf9a24c0a6cc16cf2abdc4b8b0f2531b" - integrity sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag== - dependencies: - detect-newline "^3.0.0" - jest-each@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.2.4.tgz" @@ -8225,17 +7540,6 @@ jest-each@^27.5.1: jest-util "^27.5.1" pretty-format "^27.5.1" -jest-each@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.2.tgz#e1347aff1303f4c35470827a62c029d389c5d44a" - integrity sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA== - dependencies: - "@jest/types" "^29.4.2" - chalk "^4.0.0" - jest-get-type "^29.4.2" - jest-util "^29.4.2" - pretty-format "^29.4.2" - jest-environment-jsdom@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.2.4.tgz" @@ -8262,20 +7566,6 @@ jest-environment-jsdom@^27.5.1: jest-util "^27.5.1" jsdom "^16.6.0" -jest-environment-jsdom@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.4.2.tgz#0cf95ad846949280dd58bc91a9ca463b6b232dd8" - integrity sha512-v1sH4Q0JGM+LPEGqHNM+m+uTMf3vpXpKiuDYqWUAh+0c9+nc7scGE+qTR5JuE+OOTDnwfzPgcv9sMq6zWAOaVg== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/fake-timers" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/jsdom" "^20.0.0" - "@types/node" "*" - jest-mock "^29.4.2" - jest-util "^29.4.2" - jsdom "^20.0.0" - jest-environment-node@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.2.4.tgz" @@ -8300,18 +7590,6 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" -jest-environment-node@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.2.tgz#0eab835b41e25fd0c1a72f62665fc8db08762ad2" - integrity sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/fake-timers" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - jest-mock "^29.4.2" - jest-util "^29.4.2" - jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz" @@ -8327,11 +7605,6 @@ jest-get-type@^27.5.1: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-get-type@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe" - integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg== - jest-haste-map@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.2.4.tgz" @@ -8372,25 +7645,6 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.2.tgz#9112df3f5121e643f1b2dcbaa86ab11b0b90b49a" - integrity sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw== - dependencies: - "@jest/types" "^29.4.2" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.4.2" - jest-util "^29.4.2" - jest-worker "^29.4.2" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-image-snapshot@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-5.2.0.tgz#4af046935b465f0460aa73e890717bbc25d431e9" @@ -8469,14 +7723,6 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-leak-detector@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz#8f05c6680e0cb46a1d577c0d3da9793bed3ea97b" - integrity sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA== - dependencies: - jest-get-type "^29.4.2" - pretty-format "^29.4.2" - jest-matcher-utils@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz" @@ -8506,16 +7752,6 @@ jest-matcher-utils@^27.2.4: jest-get-type "^27.0.6" pretty-format "^27.2.4" -jest-matcher-utils@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz#08d0bf5abf242e3834bec92c7ef5071732839e85" - integrity sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg== - dependencies: - chalk "^4.0.0" - jest-diff "^29.4.2" - jest-get-type "^29.4.2" - pretty-format "^29.4.2" - jest-message-util@^23.4.0: version "23.4.0" resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz" @@ -8557,21 +7793,6 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" - integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.4.2" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-mock@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.2.4.tgz" @@ -8588,15 +7809,6 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8" - integrity sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g== - dependencies: - "@jest/types" "^29.4.2" - "@types/node" "*" - jest-util "^29.4.2" - jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" @@ -8612,11 +7824,6 @@ jest-regex-util@^27.5.1: resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.2.tgz#19187cca35d301f8126cf7a021dd4dcb7b58a1ca" - integrity sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig== - jest-resolve-dependencies@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.4.tgz" @@ -8635,14 +7842,6 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" -jest-resolve-dependencies@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz#6359db606f5967b68ca8bbe9dbc07a4306c12bf7" - integrity sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg== - dependencies: - jest-regex-util "^29.4.2" - jest-snapshot "^29.4.2" - jest-resolve@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz" @@ -8684,21 +7883,6 @@ jest-resolve@^27.5.1: resolve.exports "^1.1.0" slash "^3.0.0" -jest-resolve@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.2.tgz#8831f449671d08d161fe493003f61dc9b55b808e" - integrity sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - jest-pnp-resolver "^1.2.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - jest-runner@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.2.4.tgz" @@ -8754,33 +7938,6 @@ jest-runner@^27.5.1: source-map-support "^0.5.6" throat "^6.0.1" -jest-runner@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.2.tgz#2bcecf72303369df4ef1e6e983c22a89870d5125" - integrity sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg== - dependencies: - "@jest/console" "^29.4.2" - "@jest/environment" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.4.2" - jest-environment-node "^29.4.2" - jest-haste-map "^29.4.2" - jest-leak-detector "^29.4.2" - jest-message-util "^29.4.2" - jest-resolve "^29.4.2" - jest-runtime "^29.4.2" - jest-util "^29.4.2" - jest-watcher "^29.4.2" - jest-worker "^29.4.2" - p-limit "^3.1.0" - source-map-support "0.5.13" - jest-runtime@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.2.4.tgz" @@ -8842,35 +7999,6 @@ jest-runtime@^27.5.1: slash "^3.0.0" strip-bom "^4.0.0" -jest-runtime@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.2.tgz#d86b764c5b95d76cb26ed1f32644e99de5d5c134" - integrity sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/fake-timers" "^29.4.2" - "@jest/globals" "^29.4.2" - "@jest/source-map" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - jest-message-util "^29.4.2" - jest-mock "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" - semver "^7.3.5" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-serializer@^27.0.6: version "27.0.6" resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz" @@ -8961,36 +8089,6 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" -jest-snapshot@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.2.tgz#ba1fb9abb279fd2c85109ff1757bc56b503bbb3a" - integrity sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.4.2" - graceful-fs "^4.2.9" - jest-diff "^29.4.2" - jest-get-type "^29.4.2" - jest-haste-map "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-util "^29.4.2" - natural-compare "^1.4.0" - pretty-format "^29.4.2" - semver "^7.3.5" - jest-util@^27.0.0, jest-util@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.2.4.tgz" @@ -9015,18 +8113,6 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.0.0, jest-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" - integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== - dependencies: - "@jest/types" "^29.4.2" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-validate@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.2.4.tgz" @@ -9051,18 +8137,6 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" -jest-validate@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.2.tgz#3b3f8c4910ab9a3442d2512e2175df6b3f77b915" - integrity sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q== - dependencies: - "@jest/types" "^29.4.2" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.4.2" - leven "^3.1.0" - pretty-format "^29.4.2" - jest-watcher@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.2.4.tgz" @@ -9089,20 +8163,6 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" -jest-watcher@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.2.tgz#09c0f4c9a9c7c0807fcefb1445b821c6f7953b7c" - integrity sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w== - dependencies: - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.4.2" - string-length "^4.0.1" - jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" @@ -9130,26 +8190,6 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.2.tgz#d9b2c3bafc69311d84d94e7fb45677fc8976296f" - integrity sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw== - dependencies: - "@types/node" "*" - jest-util "^29.4.2" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.2.tgz#4c2127d03a71dc187f386156ef155dbf323fb7be" - integrity sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg== - dependencies: - "@jest/core" "^29.4.2" - "@jest/types" "^29.4.2" - import-local "^3.0.2" - jest-cli "^29.4.2" - jest@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest/-/jest-27.2.4.tgz" @@ -9269,38 +8309,6 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" -jsdom@^20.0.0: - version "20.0.3" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" - integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== - dependencies: - abab "^2.0.6" - acorn "^8.8.1" - acorn-globals "^7.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.2" - decimal.js "^10.4.2" - domexception "^4.0.0" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.2" - parse5 "^7.1.1" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^4.1.2" - w3c-xmlserializer "^4.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - ws "^8.11.0" - xml-name-validator "^4.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -9350,11 +8358,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.2.2, json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - jsonc-parser@^3.0.0, jsonc-parser@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz" @@ -9658,13 +8661,6 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -9742,13 +8738,6 @@ make-fetch-happen@^9.0.1: socks-proxy-agent "^5.0.0" ssri "^8.0.0" -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" @@ -10469,11 +9458,6 @@ nwsapi@^2.2.0: resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -nwsapi@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== - oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" @@ -10673,7 +9657,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -10859,13 +9843,6 @@ parse5@^7.0.0: dependencies: entities "^4.3.0" -parse5@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== - dependencies: - entities "^4.4.0" - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -11482,15 +10459,6 @@ pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" - integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== - dependencies: - "@jest/schemas" "^29.4.2" - ansi-styles "^5.0.0" - react-is "^18.0.0" - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -11670,11 +10638,6 @@ query-string@^6.13.8: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -11721,11 +10684,6 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - read-cache@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" @@ -11969,11 +10927,6 @@ require-relative@^0.8.7: resolved "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz" integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -12001,11 +10954,6 @@ resolve.exports@^1.1.0: resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve.exports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" - integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== - resolve@1.1.7: version "1.1.7" resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" @@ -12300,13 +11248,6 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - semver-match@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/semver-match/-/semver-match-0.1.1.tgz#e7ccb31f83fd4a0e377d66387afd8ca3a329b5fc" @@ -12436,11 +11377,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - simple-peer-light@^9.10.0: version "9.10.0" resolved "https://registry.yarnpkg.com/simple-peer-light/-/simple-peer-light-9.10.0.tgz#6f3699b80e4b6d3a9374a865e1a8e497aa623afb" @@ -12547,14 +11483,6 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" @@ -13149,7 +12077,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.5, tmpl@1.0.x: +tmpl@1.0.x: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== @@ -13185,16 +12113,6 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" - tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" @@ -13210,13 +12128,6 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tr46@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== - dependencies: - punycode "^2.1.1" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -13272,20 +12183,6 @@ ts-jest@^27.1.3: semver "7.x" yargs-parser "20.x" -ts-jest@^29.0.5: - version "29.0.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.5.tgz#c5557dcec8fe434fcb8b70c3e21c6b143bfce066" - integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^29.0.0" - json5 "^2.2.3" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "^21.0.1" - ts-morph@17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" @@ -13643,11 +12540,6 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - universalify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" @@ -13692,14 +12584,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -13762,15 +12646,6 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -v8-to-istanbul@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" @@ -13850,13 +12725,6 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -w3c-xmlserializer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" - integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== - dependencies: - xml-name-validator "^4.0.0" - walker@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" @@ -13864,13 +12732,6 @@ walker@^1.0.7: dependencies: makeerror "1.0.x" -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" @@ -13893,11 +12754,6 @@ webidl-conversions@^6.1.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" @@ -13905,31 +12761,11 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-encoding@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" - integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== - dependencies: - iconv-lite "0.6.3" - whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== - dependencies: - tr46 "^3.0.0" - webidl-conversions "^7.0.0" - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" @@ -14065,14 +12901,6 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz" @@ -14131,21 +12959,11 @@ ws@^7.4.6: resolved "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== -ws@^8.11.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" - integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml-name-validator@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" - integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -14171,7 +12989,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: +yallist@^3.0.0, yallist@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -14204,7 +13022,7 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^21.0.1, yargs-parser@^21.1.1: +yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -14239,7 +13057,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.1.1, yargs@^17.3.1: +yargs@^17.1.1: version "17.6.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== From e04aa23095413a58149e6d214205c6a0b866261b Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 23:58:23 +1100 Subject: [PATCH 28/52] rename rrweb-cutter --- .changeset/config.json | 3 ++- packages/rrweb-cutter/package.json | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index 41a818e7ba..64249c6e2e 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -10,7 +10,8 @@ "rrdom-nodejs", "rrweb-player", "@rrweb/types", - "@rrweb/web-extension" + "@rrweb/web-extension", + "@rrweb/cutter" ] ], "linked": [], diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index f1ade15db5..a441a41932 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -1,6 +1,9 @@ { - "name": "rrweb-cutter", + "name": "@rrweb/cutter", "version": "0.1.7", + "publishConfig": { + "access": "public" + }, "scripts": { "dev": "vite", "test": "jest", From f27fed1fab7b6f9ddfcfd607391a90d75d57b89a Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 15 Feb 2023 00:23:36 +1100 Subject: [PATCH 29/52] upgrade vite version for web extension --- packages/web-extension/package.json | 2 +- packages/web-extension/vite.config.ts | 2 +- yarn.lock | 159 +------------------------- 3 files changed, 3 insertions(+), 160 deletions(-) diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index b439c4be46..0d3d4d5823 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -23,7 +23,7 @@ "cross-env": "^7.0.3", "type-fest": "^2.19.0", "typescript": "^4.7.3", - "vite": "^3.1.8", + "vite": "^4.1.1", "vite-plugin-web-extension": "^1.4.5", "vite-plugin-zip": "^1.0.1", "webextension-polyfill": "^0.10.0" diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index 75ee5b6394..bce5d18434 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -18,7 +18,7 @@ function useSpecialFormat( name: 'use-special-format', config(config) { const shouldUse = entriesToUse.includes( - (config.build?.lib as LibraryOptions)?.entry, + (config.build?.lib as LibraryOptions)?.entry as string, ); if (shouldUse) { config.build ??= {}; diff --git a/yarn.lock b/yarn.lock index 8e91b9076f..9718185c60 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1906,11 +1906,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== -"@esbuild/android-arm@0.15.18": - version "0.15.18" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" - integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw== - "@esbuild/android-arm@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" @@ -1956,11 +1951,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== -"@esbuild/linux-loong64@0.15.18": - version "0.15.18" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" - integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ== - "@esbuild/linux-loong64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" @@ -6882,201 +6872,101 @@ esbuild-android-64@0.14.38: resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64" integrity sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw== -esbuild-android-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5" - integrity sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA== - esbuild-android-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz#78acc80773d16007de5219ccce544c036abd50b8" integrity sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA== -esbuild-android-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz#9cc0ec60581d6ad267568f29cf4895ffdd9f2f04" - integrity sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ== - esbuild-darwin-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz#e02b1291f629ebdc2aa46fabfacc9aa28ff6aa46" integrity sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA== -esbuild-darwin-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz#428e1730ea819d500808f220fbc5207aea6d4410" - integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg== - esbuild-darwin-arm64@0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz" integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== -esbuild-darwin-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz#b6dfc7799115a2917f35970bfbc93ae50256b337" - integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA== - esbuild-freebsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz#790b8786729d4aac7be17648f9ea8e0e16475b5e" integrity sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig== -esbuild-freebsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz#4e190d9c2d1e67164619ae30a438be87d5eedaf2" - integrity sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA== - esbuild-freebsd-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz#b66340ab28c09c1098e6d9d8ff656db47d7211e6" integrity sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ== -esbuild-freebsd-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz#18a4c0344ee23bd5a6d06d18c76e2fd6d3f91635" - integrity sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA== - esbuild-linux-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz#7927f950986fd39f0ff319e92839455912b67f70" integrity sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g== -esbuild-linux-32@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz#9a329731ee079b12262b793fb84eea762e82e0ce" - integrity sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg== - esbuild-linux-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz#4893d07b229d9cfe34a2b3ce586399e73c3ac519" integrity sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q== -esbuild-linux-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz#532738075397b994467b514e524aeb520c191b6c" - integrity sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw== - esbuild-linux-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz#8442402e37d0b8ae946ac616784d9c1a2041056a" integrity sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA== -esbuild-linux-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz#5372e7993ac2da8f06b2ba313710d722b7a86e5d" - integrity sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug== - esbuild-linux-arm@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz#d5dbf32d38b7f79be0ec6b5fb2f9251fd9066986" integrity sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA== -esbuild-linux-arm@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz#e734aaf259a2e3d109d4886c9e81ec0f2fd9a9cc" - integrity sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA== - esbuild-linux-mips64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz#95081e42f698bbe35d8ccee0e3a237594b337eb5" integrity sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ== -esbuild-linux-mips64le@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz#c0487c14a9371a84eb08fab0e1d7b045a77105eb" - integrity sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ== - esbuild-linux-ppc64le@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz#dceb0a1b186f5df679618882a7990bd422089b47" integrity sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q== -esbuild-linux-ppc64le@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz#af048ad94eed0ce32f6d5a873f7abe9115012507" - integrity sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w== - esbuild-linux-riscv64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz#61fb8edb75f475f9208c4a93ab2bfab63821afd2" integrity sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ== -esbuild-linux-riscv64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz#423ed4e5927bd77f842bd566972178f424d455e6" - integrity sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg== - esbuild-linux-s390x@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz#34c7126a4937406bf6a5e69100185fd702d12fe0" integrity sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ== -esbuild-linux-s390x@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz#21d21eaa962a183bfb76312e5a01cc5ae48ce8eb" - integrity sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ== - esbuild-netbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz#322ea9937d9e529183ee281c7996b93eb38a5d95" integrity sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q== -esbuild-netbsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz#ae75682f60d08560b1fe9482bfe0173e5110b998" - integrity sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg== - esbuild-openbsd-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz#1ca29bb7a2bf09592dcc26afdb45108f08a2cdbd" integrity sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ== -esbuild-openbsd-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz#79591a90aa3b03e4863f93beec0d2bab2853d0a8" - integrity sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ== - esbuild-sunos-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz#c9446f7d8ebf45093e7bb0e7045506a88540019b" integrity sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA== -esbuild-sunos-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz#fd528aa5da5374b7e1e93d36ef9b07c3dfed2971" - integrity sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw== - esbuild-windows-32@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz#f8e9b4602fd0ccbd48e5c8d117ec0ba4040f2ad1" integrity sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw== -esbuild-windows-32@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz#0e92b66ecdf5435a76813c4bc5ccda0696f4efc3" - integrity sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ== - esbuild-windows-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz#280f58e69f78535f470905ce3e43db1746518107" integrity sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw== -esbuild-windows-64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz#0fc761d785414284fc408e7914226d33f82420d0" - integrity sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw== - esbuild-windows-arm64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz#d97e9ac0f95a4c236d9173fa9f86c983d6a53f54" integrity sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw== -esbuild-windows-arm64@0.15.18: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz#5b5bdc56d341d0922ee94965c89ee120a6a86eb7" - integrity sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ== - esbuild@^0.14.38: version "0.14.38" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz" @@ -7103,34 +6993,6 @@ esbuild@^0.14.38: esbuild-windows-64 "0.14.38" esbuild-windows-arm64 "0.14.38" -esbuild@^0.15.9: - version "0.15.18" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d" - integrity sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q== - optionalDependencies: - "@esbuild/android-arm" "0.15.18" - "@esbuild/linux-loong64" "0.15.18" - esbuild-android-64 "0.15.18" - esbuild-android-arm64 "0.15.18" - esbuild-darwin-64 "0.15.18" - esbuild-darwin-arm64 "0.15.18" - esbuild-freebsd-64 "0.15.18" - esbuild-freebsd-arm64 "0.15.18" - esbuild-linux-32 "0.15.18" - esbuild-linux-64 "0.15.18" - esbuild-linux-arm "0.15.18" - esbuild-linux-arm64 "0.15.18" - esbuild-linux-mips64le "0.15.18" - esbuild-linux-ppc64le "0.15.18" - esbuild-linux-riscv64 "0.15.18" - esbuild-linux-s390x "0.15.18" - esbuild-netbsd-64 "0.15.18" - esbuild-openbsd-64 "0.15.18" - esbuild-sunos-64 "0.15.18" - esbuild-windows-32 "0.15.18" - esbuild-windows-64 "0.15.18" - esbuild-windows-arm64 "0.15.18" - esbuild@^0.16.14: version "0.16.17" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" @@ -13057,7 +12919,7 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.4.18, postcss@^8.4.21: +postcss@^8.4.21: version "8.4.21" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== @@ -14019,13 +13881,6 @@ rollup@^2.71.1: optionalDependencies: fsevents "~2.3.2" -rollup@^2.79.1: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== - optionalDependencies: - fsevents "~2.3.2" - rollup@^3.10.0: version "3.15.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.15.0.tgz#6f4105e8c4b8145229657b74ad660b02fbfacc05" @@ -15853,18 +15708,6 @@ vite-plugin-zip@^1.0.1: dependencies: yazl "^2.5.1" -vite@^3.1.8: - version "3.2.5" - resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.5.tgz#dee5678172a8a0ab3e547ad4148c3d547f90e86a" - integrity sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ== - dependencies: - esbuild "^0.15.9" - postcss "^8.4.18" - resolve "^1.22.1" - rollup "^2.79.1" - optionalDependencies: - fsevents "~2.3.2" - vite@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vite/-/vite-4.1.1.tgz#3b18b81a4e85ce3df5cbdbf4c687d93ebf402e6b" From 60c1f7a161b4fc7caf3bb065ab55964e657de42d Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 15 Feb 2023 20:29:39 +1100 Subject: [PATCH 30/52] improve code styles and quality --- packages/rrweb-cutter/package.json | 10 ++-- packages/rrweb-cutter/src/index.ts | 82 ++++++++++++++++++++------- packages/rrweb-cutter/src/snapshot.ts | 7 +-- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index a441a41932..b25861bb3a 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/cutter", - "version": "0.1.7", + "version": "2.0.0-alpha.5", "publishConfig": { "access": "public" }, @@ -45,9 +45,9 @@ "vite-plugin-dts": "^1.7.2" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.4", - "rrdom": "^0.1.7", - "rrweb-snapshot": "^2.0.0-alpha.4", - "rrweb": "^2.0.0-alpha.4" + "@rrweb/types": "^2.0.0-alpha.5", + "rrdom": "^2.0.0-alpha.5", + "rrweb-snapshot": "^2.0.0-alpha.5", + "rrweb": "^2.0.0-alpha.5" } } diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index f62cd5f2d7..20649352d5 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,3 +1,4 @@ +import { NodeType, serializedNodeWithId } from 'rrweb-snapshot'; import type { addedNodeMutation, eventWithTime, @@ -7,7 +8,6 @@ import { IncrementalSource } from 'rrweb'; import { EventType } from 'rrweb'; import { SyncReplayer } from 'rrweb'; import snapshot from './snapshot'; -import { serializedNodeWithId } from 'rrweb-snapshot'; type CutterConfig = { points: number[]; }; @@ -39,8 +39,16 @@ export function sessionCut( index + 1 < events.length ) { const nextEvent = events[index + 1]; + + let currentTimestamp = event.timestamp; + let fullSnapshotEvent: eventWithTime | null = null; + /** + * This loop is for the situation that cutting points are in the middle gap between two events. + * These cut points share the same fullsnapshot event so there is no need to generate one for each cut point. + */ while ( cutPointIndex < validSortedTimestamp.length && + // If the next event exceed the cut point, we need to cut the events with the current replayer status. nextEvent.timestamp > validSortedTimestamp[cutPointIndex] ) { if (results.length === 0) { @@ -51,13 +59,43 @@ export function sessionCut( cutPointIndex < validSortedPoints.length ? validSortedTimestamp[cutPointIndex] : events[events.length - 1].timestamp; + if (!fullSnapshotEvent) { + let fullSnapshot = snapshot(replayer.virtualDom, { + mirror: replayer.getMirror(), + }); + if (!fullSnapshot) { + console.warn( + `Failed to generate full snapshot at timestamp ${currentTimestamp}. Using a blank snapshot as fallback.`, + ); + // Fallback to a blank snapshot. + fullSnapshot = { + type: NodeType.Document, + childNodes: [], + id: 1, + }; + } + fullSnapshotEvent = { + type: EventType.FullSnapshot, + data: { + node: fullSnapshot, + initialOffset: { + top: replayer.virtualDom.scrollTop, + left: replayer.virtualDom.scrollLeft, + }, + }, + timestamp: currentTimestamp, + }; + } + fullSnapshotEvent.timestamp = currentTimestamp; const result = cutEvents( events.slice(index + 1), replayer, - event.timestamp, + fullSnapshotEvent, + currentTimestamp, nextCutTimestamp, ); results.push(result); + currentTimestamp = nextCutTimestamp; } return cutPointIndex < validSortedTimestamp.length; } @@ -66,11 +104,22 @@ export function sessionCut( return results; } +/** + * Cut original events at the cutting point which will produce two parts. + * Only return the events before the cutting point (the previous part). + * The previous part will be built on the given full snapshot event. + * @param events - The events to be cut. + * @param replayer - The sync replayer instance. + * @param fullSnapshotEvent - The full snapshot event of the current timestamp. + * @param currentTimestamp - The current timestamp. + * @param cutTimeStamp - The timestamp to cut. + */ function cutEvents( events: eventWithTime[], replayer: SyncReplayer, + fullSnapshotEvent: eventWithTime, currentTimestamp: number, - endTimestamp: number, + cutTimeStamp: number, ) { const result: eventWithTime[] = []; if (replayer.latestMetaEvent) { @@ -78,28 +127,21 @@ function cutEvents( metaEvent.timestamp = currentTimestamp; result.push(metaEvent); } + result.push(fullSnapshotEvent); + const properDelay = 10; result.push( ...replayer.unhandledEvents.map((e) => { - e.timestamp = currentTimestamp + 10; + e.timestamp = currentTimestamp + properDelay; return e; }), ); - const fullSnapshot = snapshot(replayer.virtualDom, { - mirror: replayer.getMirror(), - }); - if (fullSnapshot) - result.push({ - type: EventType.FullSnapshot, - data: { - node: fullSnapshot, - initialOffset: { - top: 0, - left: 0, - }, - }, - timestamp: currentTimestamp, - }); - result.push(...events.filter((event) => event.timestamp <= endTimestamp)); + // TODO handle adoptedStyleSheets + // TODO handle viewportResize + // TODO handle input + // TODO handle mediaInteraction + // TODO handle scroll + + result.push(...events.filter((event) => event.timestamp <= cutTimeStamp)); return result; } diff --git a/packages/rrweb-cutter/src/snapshot.ts b/packages/rrweb-cutter/src/snapshot.ts index 6cb702bb50..6dbc4a2327 100644 --- a/packages/rrweb-cutter/src/snapshot.ts +++ b/packages/rrweb-cutter/src/snapshot.ts @@ -180,11 +180,8 @@ export function serializeNodeWithId( doc, mirror, }); - if (!_serializedNode) { - // TODO: dev only - console.warn(n, 'not serialized'); - return null; - } + if (!_serializedNode) return null; + const serializedNode = Object.assign(_serializedNode, { id: oldMeta.id }); if (onSerialize) { From aabec36834ba8a6cacc792242990e16cbcbf7d0a Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 16 Feb 2023 20:45:06 +1100 Subject: [PATCH 31/52] add support for adoptedStylesheets --- packages/rrweb-cutter/package.json | 2 + packages/rrweb-cutter/src/index.ts | 201 ++++++++++++++++++++++------- yarn.lock | 12 ++ 3 files changed, 168 insertions(+), 47 deletions(-) diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index b25861bb3a..0ecfbc3bbd 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -38,6 +38,7 @@ ], "devDependencies": { "@types/jest": "^27.4.1", + "@types/lodash.clonedeep": "^4.5.7", "jest": "^27.5.1", "ts-jest": "^27.1.3", "typescript": "^4.7.3", @@ -45,6 +46,7 @@ "vite-plugin-dts": "^1.7.2" }, "dependencies": { + "lodash.clonedeep": "^4.5.0", "@rrweb/types": "^2.0.0-alpha.5", "rrdom": "^2.0.0-alpha.5", "rrweb-snapshot": "^2.0.0-alpha.5", diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index 20649352d5..1d3d8b8eb1 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,12 +1,16 @@ import { NodeType, serializedNodeWithId } from 'rrweb-snapshot'; import type { addedNodeMutation, + adoptedStyleSheetData, eventWithTime, mousePosition, + styleDeclarationData, + styleSheetRuleData, } from '@rrweb/types'; import { IncrementalSource } from 'rrweb'; import { EventType } from 'rrweb'; import { SyncReplayer } from 'rrweb'; +import cloneDeep from 'lodash.clonedeep'; import snapshot from './snapshot'; type CutterConfig = { points: number[]; @@ -34,66 +38,48 @@ export function sessionCut( (point) => baseTime + point, ); replayer.play(({ index, event }) => { - if ( - event.timestamp <= validSortedTimestamp[cutPointIndex] && - index + 1 < events.length - ) { + const cutPoint = validSortedPoints[cutPointIndex]; + if (event.timestamp <= cutPoint && index + 1 < events.length) { + if (results.length === 0) { + results.push(events.slice(0, index + 1)); + } const nextEvent = events[index + 1]; - let currentTimestamp = event.timestamp; - let fullSnapshotEvent: eventWithTime | null = null; + let currentTimestamp = cutPoint; + let eventsCache: eventWithTime[] | null = null; /** * This loop is for the situation that cutting points are in the middle gap between two events. - * These cut points share the same fullsnapshot event so there is no need to generate one for each cut point. + * These cut points have the same cut events so there is no need to generate them for each cut point. + * We can cache the them and use them for the next cut point. */ while ( cutPointIndex < validSortedTimestamp.length && // If the next event exceed the cut point, we need to cut the events with the current replayer status. nextEvent.timestamp > validSortedTimestamp[cutPointIndex] ) { - if (results.length === 0) { - results.push(events.slice(0, index + 1)); - } cutPointIndex++; + if (eventsCache !== null) { + const timeDiff = currentTimestamp - eventsCache[0].timestamp; + const newEvents = eventsCache.map((e) => { + const newEvent = cloneDeep(e); + newEvent.timestamp += timeDiff; + return newEvent; + }); + results.push(newEvents); + continue; + } + const nextCutTimestamp = cutPointIndex < validSortedPoints.length ? validSortedTimestamp[cutPointIndex] : events[events.length - 1].timestamp; - if (!fullSnapshotEvent) { - let fullSnapshot = snapshot(replayer.virtualDom, { - mirror: replayer.getMirror(), - }); - if (!fullSnapshot) { - console.warn( - `Failed to generate full snapshot at timestamp ${currentTimestamp}. Using a blank snapshot as fallback.`, - ); - // Fallback to a blank snapshot. - fullSnapshot = { - type: NodeType.Document, - childNodes: [], - id: 1, - }; - } - fullSnapshotEvent = { - type: EventType.FullSnapshot, - data: { - node: fullSnapshot, - initialOffset: { - top: replayer.virtualDom.scrollTop, - left: replayer.virtualDom.scrollLeft, - }, - }, - timestamp: currentTimestamp, - }; - } - fullSnapshotEvent.timestamp = currentTimestamp; const result = cutEvents( events.slice(index + 1), replayer, - fullSnapshotEvent, currentTimestamp, nextCutTimestamp, ); + eventsCache = result; results.push(result); currentTimestamp = nextCutTimestamp; } @@ -107,17 +93,14 @@ export function sessionCut( /** * Cut original events at the cutting point which will produce two parts. * Only return the events before the cutting point (the previous part). - * The previous part will be built on the given full snapshot event. * @param events - The events to be cut. * @param replayer - The sync replayer instance. - * @param fullSnapshotEvent - The full snapshot event of the current timestamp. * @param currentTimestamp - The current timestamp. * @param cutTimeStamp - The timestamp to cut. */ function cutEvents( events: eventWithTime[], replayer: SyncReplayer, - fullSnapshotEvent: eventWithTime, currentTimestamp: number, cutTimeStamp: number, ) { @@ -125,26 +108,150 @@ function cutEvents( if (replayer.latestMetaEvent) { const metaEvent = replayer.latestMetaEvent; metaEvent.timestamp = currentTimestamp; - result.push(metaEvent); + result.push(cloneDeep(metaEvent)); + } + const fullsnapshotDelay = 1, + styleDelay = 2; + let fullSnapshot = snapshot(replayer.virtualDom, { + mirror: replayer.getMirror(), + }); + if (!fullSnapshot) { + console.warn( + `Failed to generate full snapshot at timestamp ${currentTimestamp}. Using a blank snapshot as fallback.`, + ); + // Fallback to a blank snapshot. + fullSnapshot = { + type: NodeType.Document, + childNodes: [], + id: 1, + }; } + const fullSnapshotEvent: eventWithTime = { + type: EventType.FullSnapshot, + data: { + node: fullSnapshot, + initialOffset: { + top: replayer.virtualDom.scrollTop, + left: replayer.virtualDom.scrollLeft, + }, + }, + timestamp: currentTimestamp, + }; result.push(fullSnapshotEvent); - const properDelay = 10; result.push( ...replayer.unhandledEvents.map((e) => { - e.timestamp = currentTimestamp + properDelay; + e.timestamp = currentTimestamp + fullsnapshotDelay; return e; }), ); - // TODO handle adoptedStyleSheets + result.push( + ...filterAdoptedStyleData( + replayer, + replayer.adoptedStyleSheets, + replayer.constructedStyleMutations, + events + .filter( + (event) => + event.timestamp <= cutTimeStamp && + event.type === EventType.IncrementalSnapshot && + event.data.source === IncrementalSource.AdoptedStyleSheet, + ) + .map((event) => event.data as adoptedStyleSheetData), + currentTimestamp + styleDelay, + ), + ); // TODO handle viewportResize // TODO handle input // TODO handle mediaInteraction // TODO handle scroll - result.push(...events.filter((event) => event.timestamp <= cutTimeStamp)); + result.push( + ...events + .filter((event) => event.timestamp <= cutTimeStamp) + .map((e) => cloneDeep(e)), + ); return result; } +/** + * Keep the adopted style data and style mutations which are still valid at this time point. + */ +function filterAdoptedStyleData( + replayer: SyncReplayer, + adoptedStyleSheetData: adoptedStyleSheetData[], + constructedStyleMutations: (styleSheetRuleData | styleDeclarationData)[], + upcomingAdoptedStyleSheets: adoptedStyleSheetData[], + timestamp: number, +) { + const events: eventWithTime[] = []; + // A map from style id without style construction to the owner node id. + const noConstructStyleIdToNodeIdMap = new Map(); + const builtStyleIds = new Set(); + const mirror = replayer.getMirror(); + /** + * Reversely iterate the adopted style sheet data to find valid styles. + * @param adoptedStyleSheetData - The adopted style sheet data. + * @param onValidData - The callback function when the data is valid. + */ + const reverseIterateAdoptedStyleData = ( + adoptedStyleSheetData: adoptedStyleSheetData[], + onValidData?: (data: adoptedStyleSheetData) => void, + ) => { + for (let i = adoptedStyleSheetData.length - 1; i >= 0; i--) { + const adoptedStyleSheet = adoptedStyleSheetData[i]; + if (mirror.has(adoptedStyleSheet.id)) { + adoptedStyleSheet.styleIds?.forEach((styleId: number) => { + if (builtStyleIds.has(styleId)) return; + noConstructStyleIdToNodeIdMap.set(styleId, adoptedStyleSheet.id); + }); + adoptedStyleSheet.styles?.forEach((styleConstruction) => { + if (noConstructStyleIdToNodeIdMap.has(styleConstruction.styleId)) + noConstructStyleIdToNodeIdMap.delete(styleConstruction.styleId); + }); + onValidData?.(adoptedStyleSheet); + } + // The owner node of style data has been removed but its style construction may still be used by other adoptedStyleSheets. + else { + if (!adoptedStyleSheet.styles) continue; + adoptedStyleSheet.styles.forEach((style) => { + if (builtStyleIds.has(style.styleId)) return; + const ownerNodeId = noConstructStyleIdToNodeIdMap.get(style.styleId); + if (!ownerNodeId) return; + noConstructStyleIdToNodeIdMap.delete(style.styleId); + builtStyleIds.add(style.styleId); + onValidData?.({ + source: IncrementalSource.AdoptedStyleSheet, + id: ownerNodeId, + styles: [style], + styleIds: [style.styleId], + }); + }); + } + } + }; + reverseIterateAdoptedStyleData(upcomingAdoptedStyleSheets); + reverseIterateAdoptedStyleData(adoptedStyleSheetData, (data) => { + events.unshift({ + type: EventType.IncrementalSnapshot, + data: cloneDeep(data), + timestamp, + }); + }); + const styleMutationDelay = 1; + constructedStyleMutations.forEach((styleConstruction) => { + if ( + styleConstruction.styleId && + builtStyleIds.has(styleConstruction.styleId) + ) + events.push({ + type: EventType.IncrementalSnapshot, + data: cloneDeep(styleConstruction), + timestamp: timestamp + styleMutationDelay, + }); + }); + return events; +} + export function pruneBranches( events: eventWithTime[], { keep }: { keep: number[] }, diff --git a/yarn.lock b/yarn.lock index 3571cc1174..c1ac0b22fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3880,6 +3880,13 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/lodash.clonedeep@^4.5.7": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197" + integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw== + dependencies: + "@types/lodash" "*" + "@types/lodash.mergewith@4.6.7": version "4.6.7" resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz#eaa65aa5872abdd282f271eae447b115b2757212" @@ -10847,6 +10854,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" From 438df999ee68121a3e35e2b1ef09b1cc701367b3 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 17 Feb 2023 18:06:08 +1100 Subject: [PATCH 32/52] refactor: split index.ts into multiple files --- packages/rrweb-cutter/src/cut-session.ts | 288 +++++++++++++ packages/rrweb-cutter/src/index.ts | 430 +------------------- packages/rrweb-cutter/src/prune-branches.ts | 174 ++++++++ packages/rrweb-cutter/vite.config.ts | 5 +- 4 files changed, 467 insertions(+), 430 deletions(-) create mode 100644 packages/rrweb-cutter/src/cut-session.ts create mode 100644 packages/rrweb-cutter/src/prune-branches.ts diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts new file mode 100644 index 0000000000..f7287bf751 --- /dev/null +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -0,0 +1,288 @@ +import { NodeType } from 'rrweb-snapshot'; +import type { + adoptedStyleSheetData, + eventWithTime, + styleDeclarationData, + styleSheetRuleData, +} from '@rrweb/types'; +import { IncrementalSource } from 'rrweb'; +import { EventType } from 'rrweb'; +import { SyncReplayer } from 'rrweb'; +import cloneDeep from 'lodash.clonedeep'; +import snapshot from './snapshot'; +type CutterConfig = { + points: number[]; +}; + +export type SessionCut = { + events: eventWithTime[]; + startTimestamp: number; + endTimestamp: number; +}; + +export function cutSession( + events: eventWithTime[], + config: CutterConfig, +): SessionCut[] { + // Events length is too short so that cutting process is not needed. + if (events.length < 2) return []; + const { points } = config; + if (!points || points.length == 0) + return [wrapCutSession(events, 0, events[events.length - 1].timestamp)]; + + events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); + const totalTime = events[events.length - 1].timestamp - events[0].timestamp; + + const validSortedPoints = getValidSortedPoints(points, totalTime); + if (validSortedPoints.length < 1) + return [wrapCutSession(events, 0, events[events.length - 1].timestamp)]; + const results: SessionCut[] = []; + const replayer = new SyncReplayer(events); + let cutPointIndex = 0; + const baseTime = events[0].timestamp; + const validSortedTimestamp = validSortedPoints.map( + (point) => baseTime + point, + ); + replayer.play(({ index, event }) => { + const cutPoint = validSortedPoints[cutPointIndex]; + if (event.timestamp <= cutPoint && index + 1 < events.length) { + if (results.length === 0) { + results.push( + wrapCutSession(events.slice(0, index + 1), 0, cutPoint - baseTime), + ); + } + const nextEvent = events[index + 1]; + + let currentTimestamp = cutPoint; + let eventsCache: eventWithTime[] | null = null; + /** + * This loop is for the situation that cutting points are in the middle gap between two events. + * These cut points have the same cut events so there is no need to generate them for each cut point. + * We can cache the them and use them for the next cut point. + */ + while ( + cutPointIndex < validSortedTimestamp.length && + // If the next event exceed the cut point, we need to cut the events with the current replayer status. + nextEvent.timestamp > validSortedTimestamp[cutPointIndex] + ) { + cutPointIndex++; + const nextCutTimestamp = + cutPointIndex < validSortedPoints.length + ? validSortedTimestamp[cutPointIndex] + : events[events.length - 1].timestamp; + if (eventsCache !== null) { + const timeDiff = currentTimestamp - eventsCache[0].timestamp; + const newEvents = eventsCache.map((e) => ({ + ...e, + timestamp: + e.timestamp < currentTimestamp + ? e.timestamp + timeDiff + : e.timestamp, + })); + results.push( + wrapCutSession(newEvents, currentTimestamp, nextCutTimestamp), + ); + continue; + } + + const result = cutEvents( + events.slice(index + 1), + replayer, + currentTimestamp, + nextCutTimestamp, + ); + eventsCache = result; + results.push( + wrapCutSession(result, currentTimestamp, nextCutTimestamp), + ); + currentTimestamp = nextCutTimestamp; + } + return cutPointIndex < validSortedTimestamp.length; + } + return false; + }); + return results; +} + +export function getValidSortedPoints(points: number[], totalTime: number) { + const validSortedPoints = []; + for (let i = 0; i < points.length; i++) { + const point = points[i]; + if (point <= 0 || point >= totalTime) continue; + validSortedPoints.push(point); + } + return validSortedPoints.sort(); +} + +function wrapCutSession( + events: eventWithTime[], + startTimestamp: number, + endTimestamp: number, +): SessionCut { + return { + events: cloneDeep(events), + startTimestamp, + endTimestamp, + }; +} + +/** + * Cut original events at the cutting point which will produce two parts. + * Only return the events before the cutting point (the previous part). + * @param events - The events to be cut. + * @param replayer - The sync replayer instance. + * @param currentTimestamp - The current timestamp. + * @param cutTimeStamp - The timestamp to cut. + */ +function cutEvents( + events: eventWithTime[], + replayer: SyncReplayer, + currentTimestamp: number, + cutTimeStamp: number, +) { + let result: eventWithTime[] = []; + if (replayer.latestMetaEvent) { + const metaEvent = { ...replayer.latestMetaEvent }; + metaEvent.timestamp = currentTimestamp; + result.push(metaEvent); + } + const fullsnapshotDelay = 1, + styleDelay = 2, + unhandledEventDelay = 2; + let fullSnapshot = snapshot(replayer.virtualDom, { + mirror: replayer.getMirror(), + }); + if (!fullSnapshot) { + console.warn( + `Failed to generate full snapshot at timestamp ${currentTimestamp}. Using a blank snapshot as fallback.`, + ); + // Fallback to a blank snapshot. + fullSnapshot = { + type: NodeType.Document, + childNodes: [], + id: 1, + }; + } + const fullSnapshotEvent: eventWithTime = { + type: EventType.FullSnapshot, + data: { + node: fullSnapshot, + initialOffset: { + top: replayer.virtualDom.scrollTop, + left: replayer.virtualDom.scrollLeft, + }, + }, + timestamp: currentTimestamp + fullsnapshotDelay, + }; + result.push(fullSnapshotEvent); + result = result.concat( + replayer.unhandledEvents.map((e) => ({ + ...e, + timestamp: currentTimestamp + unhandledEventDelay, + })), + ); + result = result.concat( + filterAdoptedStyleData( + replayer, + replayer.adoptedStyleSheets, + replayer.constructedStyleMutations, + events + .filter( + (event) => + event.timestamp <= cutTimeStamp && + event.type === EventType.IncrementalSnapshot && + event.data.source === IncrementalSource.AdoptedStyleSheet, + ) + .map((event) => event.data as adoptedStyleSheetData), + currentTimestamp + styleDelay, + ), + ); + // TODO handle viewportResize + // TODO handle input + // TODO handle mediaInteraction + // TODO handle scroll + + result = result.concat( + events.filter((event) => event.timestamp <= cutTimeStamp), + ); + return result; +} + +/** + * Keep the adopted style data and style mutations which are still valid at this time point. + */ +function filterAdoptedStyleData( + replayer: SyncReplayer, + adoptedStyleSheetData: adoptedStyleSheetData[], + constructedStyleMutations: (styleSheetRuleData | styleDeclarationData)[], + upcomingAdoptedStyleSheets: adoptedStyleSheetData[], + timestamp: number, +) { + const events: eventWithTime[] = []; + // A map from style id without style construction to the owner node id. + const noConstructStyleIdToNodeIdMap = new Map(); + const builtStyleIds = new Set(); + const mirror = replayer.getMirror(); + /** + * Reversely iterate the adopted style sheet data to find valid styles. + * @param adoptedStyleSheetData - The adopted style sheet data. + * @param onValidData - The callback function when the data is valid. + */ + const reverseIterateAdoptedStyleData = ( + adoptedStyleSheetData: adoptedStyleSheetData[], + onValidData?: (data: adoptedStyleSheetData) => void, + ) => { + for (let i = adoptedStyleSheetData.length - 1; i >= 0; i--) { + const adoptedStyleSheet = adoptedStyleSheetData[i]; + if (mirror.has(adoptedStyleSheet.id)) { + adoptedStyleSheet.styleIds?.forEach((styleId: number) => { + if (builtStyleIds.has(styleId)) return; + noConstructStyleIdToNodeIdMap.set(styleId, adoptedStyleSheet.id); + }); + adoptedStyleSheet.styles?.forEach((styleConstruction) => { + if (noConstructStyleIdToNodeIdMap.has(styleConstruction.styleId)) + noConstructStyleIdToNodeIdMap.delete(styleConstruction.styleId); + }); + onValidData?.(adoptedStyleSheet); + } + // The owner node of style data has been removed but its style construction may still be used by other adoptedStyleSheets. + else { + if (!adoptedStyleSheet.styles) continue; + adoptedStyleSheet.styles.forEach((style) => { + if (builtStyleIds.has(style.styleId)) return; + const ownerNodeId = noConstructStyleIdToNodeIdMap.get(style.styleId); + if (!ownerNodeId) return; + noConstructStyleIdToNodeIdMap.delete(style.styleId); + builtStyleIds.add(style.styleId); + onValidData?.({ + source: IncrementalSource.AdoptedStyleSheet, + id: ownerNodeId, + styles: [style], + styleIds: [style.styleId], + }); + }); + } + } + }; + reverseIterateAdoptedStyleData(upcomingAdoptedStyleSheets); + reverseIterateAdoptedStyleData(adoptedStyleSheetData, (data) => { + events.unshift({ + type: EventType.IncrementalSnapshot, + data, + timestamp, + }); + }); + const styleMutationDelay = 1; + constructedStyleMutations.forEach((styleConstruction) => { + if ( + styleConstruction.styleId && + builtStyleIds.has(styleConstruction.styleId) + ) + events.push({ + type: EventType.IncrementalSnapshot, + data: styleConstruction, + timestamp: timestamp + styleMutationDelay, + }); + }); + return events; +} diff --git a/packages/rrweb-cutter/src/index.ts b/packages/rrweb-cutter/src/index.ts index 1d3d8b8eb1..026c9d2d13 100644 --- a/packages/rrweb-cutter/src/index.ts +++ b/packages/rrweb-cutter/src/index.ts @@ -1,428 +1,2 @@ -import { NodeType, serializedNodeWithId } from 'rrweb-snapshot'; -import type { - addedNodeMutation, - adoptedStyleSheetData, - eventWithTime, - mousePosition, - styleDeclarationData, - styleSheetRuleData, -} from '@rrweb/types'; -import { IncrementalSource } from 'rrweb'; -import { EventType } from 'rrweb'; -import { SyncReplayer } from 'rrweb'; -import cloneDeep from 'lodash.clonedeep'; -import snapshot from './snapshot'; -type CutterConfig = { - points: number[]; -}; - -export function sessionCut( - events: eventWithTime[], - config: CutterConfig, -): eventWithTime[][] { - // Events length is too short so that cutting process is not needed. - if (events.length < 2) return [events]; - const { points } = config; - if (!points || points.length == 0) return [events]; - - events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); - const totalTime = events[events.length - 1].timestamp - events[0].timestamp; - - const validSortedPoints = getValidSortedPoints(points, totalTime); - if (validSortedPoints.length < 1) return [events]; - const results: eventWithTime[][] = []; - const replayer = new SyncReplayer(events); - let cutPointIndex = 0; - const baseTime = events[0].timestamp; - const validSortedTimestamp = validSortedPoints.map( - (point) => baseTime + point, - ); - replayer.play(({ index, event }) => { - const cutPoint = validSortedPoints[cutPointIndex]; - if (event.timestamp <= cutPoint && index + 1 < events.length) { - if (results.length === 0) { - results.push(events.slice(0, index + 1)); - } - const nextEvent = events[index + 1]; - - let currentTimestamp = cutPoint; - let eventsCache: eventWithTime[] | null = null; - /** - * This loop is for the situation that cutting points are in the middle gap between two events. - * These cut points have the same cut events so there is no need to generate them for each cut point. - * We can cache the them and use them for the next cut point. - */ - while ( - cutPointIndex < validSortedTimestamp.length && - // If the next event exceed the cut point, we need to cut the events with the current replayer status. - nextEvent.timestamp > validSortedTimestamp[cutPointIndex] - ) { - cutPointIndex++; - if (eventsCache !== null) { - const timeDiff = currentTimestamp - eventsCache[0].timestamp; - const newEvents = eventsCache.map((e) => { - const newEvent = cloneDeep(e); - newEvent.timestamp += timeDiff; - return newEvent; - }); - results.push(newEvents); - continue; - } - - const nextCutTimestamp = - cutPointIndex < validSortedPoints.length - ? validSortedTimestamp[cutPointIndex] - : events[events.length - 1].timestamp; - const result = cutEvents( - events.slice(index + 1), - replayer, - currentTimestamp, - nextCutTimestamp, - ); - eventsCache = result; - results.push(result); - currentTimestamp = nextCutTimestamp; - } - return cutPointIndex < validSortedTimestamp.length; - } - return false; - }); - return results; -} - -/** - * Cut original events at the cutting point which will produce two parts. - * Only return the events before the cutting point (the previous part). - * @param events - The events to be cut. - * @param replayer - The sync replayer instance. - * @param currentTimestamp - The current timestamp. - * @param cutTimeStamp - The timestamp to cut. - */ -function cutEvents( - events: eventWithTime[], - replayer: SyncReplayer, - currentTimestamp: number, - cutTimeStamp: number, -) { - const result: eventWithTime[] = []; - if (replayer.latestMetaEvent) { - const metaEvent = replayer.latestMetaEvent; - metaEvent.timestamp = currentTimestamp; - result.push(cloneDeep(metaEvent)); - } - const fullsnapshotDelay = 1, - styleDelay = 2; - let fullSnapshot = snapshot(replayer.virtualDom, { - mirror: replayer.getMirror(), - }); - if (!fullSnapshot) { - console.warn( - `Failed to generate full snapshot at timestamp ${currentTimestamp}. Using a blank snapshot as fallback.`, - ); - // Fallback to a blank snapshot. - fullSnapshot = { - type: NodeType.Document, - childNodes: [], - id: 1, - }; - } - const fullSnapshotEvent: eventWithTime = { - type: EventType.FullSnapshot, - data: { - node: fullSnapshot, - initialOffset: { - top: replayer.virtualDom.scrollTop, - left: replayer.virtualDom.scrollLeft, - }, - }, - timestamp: currentTimestamp, - }; - result.push(fullSnapshotEvent); - result.push( - ...replayer.unhandledEvents.map((e) => { - e.timestamp = currentTimestamp + fullsnapshotDelay; - return e; - }), - ); - result.push( - ...filterAdoptedStyleData( - replayer, - replayer.adoptedStyleSheets, - replayer.constructedStyleMutations, - events - .filter( - (event) => - event.timestamp <= cutTimeStamp && - event.type === EventType.IncrementalSnapshot && - event.data.source === IncrementalSource.AdoptedStyleSheet, - ) - .map((event) => event.data as adoptedStyleSheetData), - currentTimestamp + styleDelay, - ), - ); - // TODO handle viewportResize - // TODO handle input - // TODO handle mediaInteraction - // TODO handle scroll - - result.push( - ...events - .filter((event) => event.timestamp <= cutTimeStamp) - .map((e) => cloneDeep(e)), - ); - return result; -} - -/** - * Keep the adopted style data and style mutations which are still valid at this time point. - */ -function filterAdoptedStyleData( - replayer: SyncReplayer, - adoptedStyleSheetData: adoptedStyleSheetData[], - constructedStyleMutations: (styleSheetRuleData | styleDeclarationData)[], - upcomingAdoptedStyleSheets: adoptedStyleSheetData[], - timestamp: number, -) { - const events: eventWithTime[] = []; - // A map from style id without style construction to the owner node id. - const noConstructStyleIdToNodeIdMap = new Map(); - const builtStyleIds = new Set(); - const mirror = replayer.getMirror(); - /** - * Reversely iterate the adopted style sheet data to find valid styles. - * @param adoptedStyleSheetData - The adopted style sheet data. - * @param onValidData - The callback function when the data is valid. - */ - const reverseIterateAdoptedStyleData = ( - adoptedStyleSheetData: adoptedStyleSheetData[], - onValidData?: (data: adoptedStyleSheetData) => void, - ) => { - for (let i = adoptedStyleSheetData.length - 1; i >= 0; i--) { - const adoptedStyleSheet = adoptedStyleSheetData[i]; - if (mirror.has(adoptedStyleSheet.id)) { - adoptedStyleSheet.styleIds?.forEach((styleId: number) => { - if (builtStyleIds.has(styleId)) return; - noConstructStyleIdToNodeIdMap.set(styleId, adoptedStyleSheet.id); - }); - adoptedStyleSheet.styles?.forEach((styleConstruction) => { - if (noConstructStyleIdToNodeIdMap.has(styleConstruction.styleId)) - noConstructStyleIdToNodeIdMap.delete(styleConstruction.styleId); - }); - onValidData?.(adoptedStyleSheet); - } - // The owner node of style data has been removed but its style construction may still be used by other adoptedStyleSheets. - else { - if (!adoptedStyleSheet.styles) continue; - adoptedStyleSheet.styles.forEach((style) => { - if (builtStyleIds.has(style.styleId)) return; - const ownerNodeId = noConstructStyleIdToNodeIdMap.get(style.styleId); - if (!ownerNodeId) return; - noConstructStyleIdToNodeIdMap.delete(style.styleId); - builtStyleIds.add(style.styleId); - onValidData?.({ - source: IncrementalSource.AdoptedStyleSheet, - id: ownerNodeId, - styles: [style], - styleIds: [style.styleId], - }); - }); - } - } - }; - reverseIterateAdoptedStyleData(upcomingAdoptedStyleSheets); - reverseIterateAdoptedStyleData(adoptedStyleSheetData, (data) => { - events.unshift({ - type: EventType.IncrementalSnapshot, - data: cloneDeep(data), - timestamp, - }); - }); - const styleMutationDelay = 1; - constructedStyleMutations.forEach((styleConstruction) => { - if ( - styleConstruction.styleId && - builtStyleIds.has(styleConstruction.styleId) - ) - events.push({ - type: EventType.IncrementalSnapshot, - data: cloneDeep(styleConstruction), - timestamp: timestamp + styleMutationDelay, - }); - }); - return events; -} - -export function pruneBranches( - events: eventWithTime[], - { keep }: { keep: number[] }, -): eventWithTime[] { - const result: eventWithTime[] = []; - const replayer = new SyncReplayer(events); - const treeSet = new Set(keep); - replayer.reversePlay(({ event }) => { - if (event.type === EventType.FullSnapshot) { - const { node } = event.data; - const tree = getTreeForId(treeSet, node, keep); - tree.forEach((id) => treeSet.add(id)); - } else if (event.type === EventType.IncrementalSnapshot) { - if (event.data.source === IncrementalSource.Mutation) { - const { adds, removes } = event.data; - removes.forEach((remove) => { - if (treeSet.has(remove.id)) treeSet.add(remove.parentId); - }); - adds.forEach((add) => { - const tree = getTreeForId(treeSet, add.node, keep); - if (tree.size) { - treeSet.add(add.parentId); - tree.forEach((id) => treeSet.add(id)); - } else if ( - 'childNodes' in add.node && - add.node.childNodes.length > 0 - ) { - const tree = getTreeForId(treeSet, add.node, keep); - if (tree.size) treeSet.add(add.parentId); - tree.forEach((id) => treeSet.add(id)); - } - }); - } - } - return true; - }); - - replayer.play(({ event }) => { - if ( - [EventType.Meta, EventType.Load, EventType.DomContentLoaded].includes( - event.type, - ) - ) { - result.push(event); - } else if (event.type === EventType.FullSnapshot) { - const { node } = event.data; - const prunedNode = reconstructTreeWithIds(node, treeSet); - if (prunedNode) - result.push({ - ...event, - data: { - ...event.data, - node: prunedNode, - }, - } as eventWithTime); - } else if (event.type === EventType.IncrementalSnapshot) { - if ('positions' in event.data) { - const { positions } = event.data; - const prunedPositions: mousePosition[] = positions.filter((p) => - treeSet.has(p.id), - ); - if (prunedPositions.length > 0) - result.push({ - ...event, - data: { - ...event.data, - positions: prunedPositions, - }, - } as eventWithTime); - } else if ('id' in event.data) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (treeSet.has(event.data.id!)) result.push(event); - } else if (event.data.source === IncrementalSource.Mutation) { - const { removes, adds, texts, attributes } = event.data; - const prunedRemoves = removes.filter((remove) => - treeSet.has(remove.id), - ); - const prunedAdds = adds - .map((add) => - treeSet.has(add.parentId) && keep.includes(add.parentId) - ? add - : { - ...add, - node: reconstructTreeWithIds(add.node, treeSet), - }, - ) - .filter((add) => Boolean(add.node)) as addedNodeMutation[]; - const prunedTexts = texts.filter((text) => treeSet.has(text.id)); - const prunedAttributes = attributes.filter((attr) => - treeSet.has(attr.id), - ); - if ( - prunedRemoves.length > 0 || - prunedAdds.length > 0 || - prunedTexts.length > 0 || - prunedAttributes.length > 0 - ) - result.push({ - ...event, - data: { - ...event.data, - removes: prunedRemoves, - adds: prunedAdds, - texts: prunedTexts, - attributes: prunedAttributes, - }, - } as eventWithTime); - } - } - return true; - }); - return result; -} - -export function getTreeForId( - treeSet: Set, - node: serializedNodeWithId, - keepIds: number[], -): Set { - const results = new Set(); - if (treeSet.has(node.id)) { - getIdsInNode(node, keepIds).forEach((id) => results.add(id)); - } else if ('childNodes' in node) { - for (let i = 0; i < node.childNodes.length; i++) { - const child = node.childNodes[i]; - const childTree = getTreeForId(treeSet, child, keepIds); - if (childTree.size > 0) { - results.add(node.id); - childTree.forEach((id) => results.add(id)); - } - } - } - return results; -} - -export function getIdsInNode( - node: serializedNodeWithId, - keepIds: number[], -): Array { - const results: number[] = []; - results.push(node.id); - if (keepIds.includes(node.id) && 'childNodes' in node) { - for (let i = 0; i < node.childNodes.length; i++) { - const child = node.childNodes[i]; - results.push(...getIdsInNode(child, keepIds)); - } - } - return results; -} - -export function reconstructTreeWithIds( - node: serializedNodeWithId, - ids: Set, -): serializedNodeWithId | undefined { - if (ids.has(node.id)) { - if ('childNodes' in node) { - node.childNodes = node.childNodes - .map((child) => reconstructTreeWithIds(child, ids)) - .filter(Boolean) as serializedNodeWithId[]; - } - return node; - } - return undefined; -} - -export function getValidSortedPoints(points: number[], totalTime: number) { - const validSortedPoints = []; - for (let i = 0; i < points.length; i++) { - const point = points[i]; - if (point <= 0 || point >= totalTime) continue; - validSortedPoints.push(point); - } - return validSortedPoints.sort(); -} +export * from './cut-session'; +export * from './prune-branches'; diff --git a/packages/rrweb-cutter/src/prune-branches.ts b/packages/rrweb-cutter/src/prune-branches.ts new file mode 100644 index 0000000000..4aed7c971a --- /dev/null +++ b/packages/rrweb-cutter/src/prune-branches.ts @@ -0,0 +1,174 @@ +import { serializedNodeWithId } from 'rrweb-snapshot'; +import type { + addedNodeMutation, + eventWithTime, + mousePosition, +} from '@rrweb/types'; +import { IncrementalSource } from 'rrweb'; +import { EventType } from 'rrweb'; +import { SyncReplayer } from 'rrweb'; + +export function pruneBranches( + events: eventWithTime[], + { keep }: { keep: number[] }, +): eventWithTime[] { + const result: eventWithTime[] = []; + const replayer = new SyncReplayer(events); + const treeSet = new Set(keep); + replayer.reversePlay(({ event }) => { + if (event.type === EventType.FullSnapshot) { + const { node } = event.data; + const tree = getTreeForId(treeSet, node, keep); + tree.forEach((id) => treeSet.add(id)); + } else if (event.type === EventType.IncrementalSnapshot) { + if (event.data.source === IncrementalSource.Mutation) { + const { adds, removes } = event.data; + removes.forEach((remove) => { + if (treeSet.has(remove.id)) treeSet.add(remove.parentId); + }); + adds.forEach((add) => { + const tree = getTreeForId(treeSet, add.node, keep); + if (tree.size) { + treeSet.add(add.parentId); + tree.forEach((id) => treeSet.add(id)); + } else if ( + 'childNodes' in add.node && + add.node.childNodes.length > 0 + ) { + const tree = getTreeForId(treeSet, add.node, keep); + if (tree.size) treeSet.add(add.parentId); + tree.forEach((id) => treeSet.add(id)); + } + }); + } + } + return true; + }); + + replayer.play(({ event }) => { + if ( + [EventType.Meta, EventType.Load, EventType.DomContentLoaded].includes( + event.type, + ) + ) { + result.push(event); + } else if (event.type === EventType.FullSnapshot) { + const { node } = event.data; + const prunedNode = reconstructTreeWithIds(node, treeSet); + if (prunedNode) + result.push({ + ...event, + data: { + ...event.data, + node: prunedNode, + }, + } as eventWithTime); + } else if (event.type === EventType.IncrementalSnapshot) { + if ('positions' in event.data) { + const { positions } = event.data; + const prunedPositions: mousePosition[] = positions.filter((p) => + treeSet.has(p.id), + ); + if (prunedPositions.length > 0) + result.push({ + ...event, + data: { + ...event.data, + positions: prunedPositions, + }, + } as eventWithTime); + } else if ('id' in event.data) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (treeSet.has(event.data.id!)) result.push(event); + } else if (event.data.source === IncrementalSource.Mutation) { + const { removes, adds, texts, attributes } = event.data; + const prunedRemoves = removes.filter((remove) => + treeSet.has(remove.id), + ); + const prunedAdds = adds + .map((add) => + treeSet.has(add.parentId) && keep.includes(add.parentId) + ? add + : { + ...add, + node: reconstructTreeWithIds(add.node, treeSet), + }, + ) + .filter((add) => Boolean(add.node)) as addedNodeMutation[]; + const prunedTexts = texts.filter((text) => treeSet.has(text.id)); + const prunedAttributes = attributes.filter((attr) => + treeSet.has(attr.id), + ); + if ( + prunedRemoves.length > 0 || + prunedAdds.length > 0 || + prunedTexts.length > 0 || + prunedAttributes.length > 0 + ) + result.push({ + ...event, + data: { + ...event.data, + removes: prunedRemoves, + adds: prunedAdds, + texts: prunedTexts, + attributes: prunedAttributes, + }, + } as eventWithTime); + } + } + return true; + }); + return result; +} + +export function getTreeForId( + treeSet: Set, + node: serializedNodeWithId, + keepIds: number[], +): Set { + const results = new Set(); + if (treeSet.has(node.id)) { + getIdsInNode(node, keepIds).forEach((id) => results.add(id)); + } else if ('childNodes' in node) { + for (let i = 0; i < node.childNodes.length; i++) { + const child = node.childNodes[i]; + const childTree = getTreeForId(treeSet, child, keepIds); + if (childTree.size > 0) { + results.add(node.id); + childTree.forEach((id) => results.add(id)); + } + } + } + return results; +} + +export function getIdsInNode( + node: serializedNodeWithId, + keepIds: number[], +): Array { + const results: number[] = []; + results.push(node.id); + if (keepIds.includes(node.id) && 'childNodes' in node) { + for (let i = 0; i < node.childNodes.length; i++) { + const child = node.childNodes[i]; + results.push(...getIdsInNode(child, keepIds)); + } + } + return results; +} + +export function reconstructTreeWithIds( + node: serializedNodeWithId, + ids: Set, +): serializedNodeWithId | undefined { + if (ids.has(node.id)) { + if ('childNodes' in node) { + node.childNodes = node.childNodes + .map((child) => reconstructTreeWithIds(child, ids)) + .filter(Boolean) as serializedNodeWithId[]; + } + return node; + } + return undefined; +} diff --git a/packages/rrweb-cutter/vite.config.ts b/packages/rrweb-cutter/vite.config.ts index d7910ee321..7b4d485ba9 100644 --- a/packages/rrweb-cutter/vite.config.ts +++ b/packages/rrweb-cutter/vite.config.ts @@ -1,4 +1,3 @@ -import path from 'path'; import dts from 'vite-plugin-dts'; /** * @type {import('vite').UserConfig} @@ -6,7 +5,7 @@ import dts from 'vite-plugin-dts'; export default { build: { lib: { - entry: path.resolve(__dirname, 'src/index.ts'), + entry: 'src/index.ts', name: 'rrwebCutter', fileName: 'index', formats: ['es', 'cjs', 'umd', 'iife'], @@ -15,6 +14,8 @@ export default { minify: true, sourcemap: true, + + emptyOutDir: true, }, plugins: [dts()], }; From 7454590a0f2d52693cabf344fe6a673a6728c5b3 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 20 Feb 2023 12:58:15 +1100 Subject: [PATCH 33/52] add support for many incremental event type e.g. mousemove, scroll, input, selection, media change, and canvas --- packages/rrweb-cutter/src/cut-session.ts | 121 ++++++++++++++++++--- packages/rrweb-cutter/src/snapshot.ts | 3 + packages/rrweb-cutter/tsconfig.json | 15 +-- packages/rrweb/src/replay/sync-replayer.ts | 5 + 4 files changed, 117 insertions(+), 27 deletions(-) diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index f7287bf751..7071e07df1 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -1,10 +1,18 @@ import { NodeType } from 'rrweb-snapshot'; -import type { +import { adoptedStyleSheetData, eventWithTime, + MediaInteractions, styleDeclarationData, styleSheetRuleData, } from '@rrweb/types'; +import { + RRElement, + RRNode, + RRCanvasElement, + RRStyleElement, + RRMediaElement, +} from 'rrdom'; import { IncrementalSource } from 'rrweb'; import { EventType } from 'rrweb'; import { SyncReplayer } from 'rrweb'; @@ -85,16 +93,14 @@ export function cutSession( continue; } - const result = cutEvents( + const session = cutEvents( events.slice(index + 1), replayer, currentTimestamp, nextCutTimestamp, ); - eventsCache = result; - results.push( - wrapCutSession(result, currentTimestamp, nextCutTimestamp), - ); + results.push(session); + eventsCache = session.events; currentTimestamp = nextCutTimestamp; } return cutPointIndex < validSortedTimestamp.length; @@ -128,7 +134,7 @@ function wrapCutSession( /** * Cut original events at the cutting point which will produce two parts. - * Only return the events before the cutting point (the previous part). + * Only return the deep cloned cut session before the cutting point (the previous part). * @param events - The events to be cut. * @param replayer - The sync replayer instance. * @param currentTimestamp - The current timestamp. @@ -147,10 +153,69 @@ function cutEvents( result.push(metaEvent); } const fullsnapshotDelay = 1, - styleDelay = 2, - unhandledEventDelay = 2; + IncrementalEventDelay = 2; let fullSnapshot = snapshot(replayer.virtualDom, { mirror: replayer.getMirror(), + onSerialize: (n: RRNode) => { + const timestamp = currentTimestamp + fullsnapshotDelay; + if (n.RRNodeType === NodeType.Element) { + const rrElement = n as RRElement; + rrElement.inputData && + result.push({ + type: EventType.IncrementalSnapshot, + data: rrElement.inputData, + timestamp, + }); + rrElement.scrollData && + result.push({ + type: EventType.IncrementalSnapshot, + data: rrElement.scrollData, + timestamp, + }); + if (rrElement instanceof RRCanvasElement) + rrElement.canvasMutations.forEach((canvasData) => + result.push({ + type: EventType.IncrementalSnapshot, + data: canvasData.mutation, + timestamp, + }), + ); + else if (rrElement instanceof RRStyleElement) + rrElement.rules.forEach((styleRule) => + result.push({ + type: EventType.IncrementalSnapshot, + data: styleRule, + timestamp, + }), + ); + else if (rrElement instanceof RRMediaElement) { + (rrElement.volume !== undefined || rrElement.muted !== undefined) && + result.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MediaInteraction, + type: MediaInteractions.VolumeChange, + volume: rrElement.volume, + muted: rrElement.muted, + id: replayer.getMirror().getId(n), + }, + timestamp, + }); + rrElement.playbackRate !== undefined && + result.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MediaInteraction, + type: MediaInteractions.RateChange, + playbackRate: rrElement.playbackRate, + id: replayer.getMirror().getId(n), + }, + timestamp, + }); + } + } + // TODO handle iframe + }, }); if (!fullSnapshot) { console.warn( @@ -178,7 +243,7 @@ function cutEvents( result = result.concat( replayer.unhandledEvents.map((e) => ({ ...e, - timestamp: currentTimestamp + unhandledEventDelay, + timestamp: currentTimestamp + IncrementalEventDelay, })), ); result = result.concat( @@ -194,18 +259,42 @@ function cutEvents( event.data.source === IncrementalSource.AdoptedStyleSheet, ) .map((event) => event.data as adoptedStyleSheetData), - currentTimestamp + styleDelay, + currentTimestamp + IncrementalEventDelay, ), ); - // TODO handle viewportResize - // TODO handle input - // TODO handle mediaInteraction - // TODO handle scroll + + replayer.mousePos && + result.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseMove, + positions: [ + { + x: replayer.mousePos.x, + y: replayer.mousePos.y, + id: replayer.mousePos.id, + timeOffset: 0, + }, + ], + }, + timestamp: currentTimestamp + IncrementalEventDelay, + }); + + replayer.lastSelectionData && + result.push({ + type: EventType.IncrementalSnapshot, + data: replayer.lastSelectionData, + timestamp: currentTimestamp + IncrementalEventDelay, + }); result = result.concat( events.filter((event) => event.timestamp <= cutTimeStamp), ); - return result; + const session = wrapCutSession(result, currentTimestamp, cutTimeStamp); + // TODO handle node mutations (hard to do) + // TODO handle sequential plugin + // TODO handle custom event + return session; } /** diff --git a/packages/rrweb-cutter/src/snapshot.ts b/packages/rrweb-cutter/src/snapshot.ts index 6dbc4a2327..48200a1024 100644 --- a/packages/rrweb-cutter/src/snapshot.ts +++ b/packages/rrweb-cutter/src/snapshot.ts @@ -6,6 +6,7 @@ import type { IRRDocumentType, IRRElement, IRRNode, + RRCanvasElement, RRIFrameElement, RRMediaElement, } from 'rrdom'; @@ -122,6 +123,8 @@ function serializeElementNode( // the child text is inserted and untracked by the rrweb replayer if (n.childNodes[0] && mirror.getId(n.childNodes[0]) < 0) attributes._cssText = n.textContent || ''; + } else if (tagName === 'canvas' && (n as RRCanvasElement).rr_dataURL) { + attributes.rr_dataURL = (n as RRCanvasElement).rr_dataURL as string; } if (n.scrollLeft) { attributes.rr_scrollLeft = n.scrollLeft; diff --git a/packages/rrweb-cutter/tsconfig.json b/packages/rrweb-cutter/tsconfig.json index 8a26bf918e..98cc10e251 100644 --- a/packages/rrweb-cutter/tsconfig.json +++ b/packages/rrweb-cutter/tsconfig.json @@ -6,29 +6,22 @@ "declarationDir": "dist/types", "target": "ES5", "module": "commonjs", - "lib": [ - "ES6", - "DOM" - ], + "lib": ["ES6", "DOM"], "moduleResolution": "Node", "sourceMap": true, "strictNullChecks": true, "removeComments": true, "preserveConstEnums": true, "resolveJsonModule": true, - "isolatedModules": true, + "isolatedModules": false, "esModuleInterop": true, "noImplicitReturns": true, "noImplicitAny": true, "skipLibCheck": true }, "compileOnSave": true, - "include": [ - "src" - ], - "exclude": [ - "test" - ], + "include": ["src"], + "exclude": ["test"], "references": [ { "path": "../types" diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index 0c4369580e..1477a6ea21 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -527,6 +527,11 @@ export class SyncReplayer { width: d.width, height: d.height, }); + if (!this.latestMetaEvent) break; + Object.assign(this.latestMetaEvent.data as metaEvent, { + width: d.width, + height: d.height, + }); break; case IncrementalSource.Input: { /** From 67e58bba257013f438ff7dcf5345070198ec9574 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 20 Feb 2023 15:04:13 +1100 Subject: [PATCH 34/52] handle custom events --- packages/rrweb-cutter/src/cut-session.ts | 75 +++++++++++++++++++----- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index 7071e07df1..bda357dfad 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -13,13 +13,24 @@ import { RRStyleElement, RRMediaElement, } from 'rrdom'; -import { IncrementalSource } from 'rrweb'; -import { EventType } from 'rrweb'; -import { SyncReplayer } from 'rrweb'; +import { IncrementalSource, EventType, SyncReplayer } from 'rrweb'; +import { playerConfig } from 'rrweb/typings/types'; import cloneDeep from 'lodash.clonedeep'; import snapshot from './snapshot'; -type CutterConfig = { +export type CutterConfig = { points: number[]; + replayerConfig?: Partial; + onSessionCut?: (context: { + replayer: SyncReplayer; + cutSession: SessionCut; + originalEvents: eventWithTime[]; + }) => SessionCut; + customEventsHandler?: (context: { + replayer: SyncReplayer; + events: eventWithTime[]; + currentTimestamp: number; + cutTimestamp: number; + }) => eventWithTime[]; }; export type SessionCut = { @@ -45,7 +56,9 @@ export function cutSession( if (validSortedPoints.length < 1) return [wrapCutSession(events, 0, events[events.length - 1].timestamp)]; const results: SessionCut[] = []; - const replayer = new SyncReplayer(events); + const replayer = new SyncReplayer(events, { + ...config.replayerConfig, + }); let cutPointIndex = 0; const baseTime = events[0].timestamp; const validSortedTimestamp = validSortedPoints.map( @@ -94,10 +107,13 @@ export function cutSession( } const session = cutEvents( - events.slice(index + 1), + events + .slice(index + 1) + .filter((e) => e.timestamp <= nextCutTimestamp), replayer, currentTimestamp, nextCutTimestamp, + config, ); results.push(session); eventsCache = session.events; @@ -138,13 +154,14 @@ function wrapCutSession( * @param events - The events to be cut. * @param replayer - The sync replayer instance. * @param currentTimestamp - The current timestamp. - * @param cutTimeStamp - The timestamp to cut. + * @param cutTimestamp - The timestamp to cut. */ function cutEvents( events: eventWithTime[], replayer: SyncReplayer, currentTimestamp: number, - cutTimeStamp: number, + cutTimestamp: number, + config: CutterConfig, ) { let result: eventWithTime[] = []; if (replayer.latestMetaEvent) { @@ -254,7 +271,7 @@ function cutEvents( events .filter( (event) => - event.timestamp <= cutTimeStamp && + event.timestamp <= cutTimestamp && event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.AdoptedStyleSheet, ) @@ -286,14 +303,38 @@ function cutEvents( data: replayer.lastSelectionData, timestamp: currentTimestamp + IncrementalEventDelay, }); - - result = result.concat( - events.filter((event) => event.timestamp <= cutTimeStamp), + if (config.customEventsHandler) + try { + result = result.concat( + config.customEventsHandler({ + events, + replayer, + currentTimestamp, + cutTimestamp, + }), + ); + } catch (e) { + warn(config, 'Custom Event Handler error: ', e); + } + result = result.concat(events); + let session = wrapCutSession( + result, + currentTimestamp + IncrementalEventDelay, + cutTimestamp, ); - const session = wrapCutSession(result, currentTimestamp, cutTimeStamp); + if (config.onSessionCut) + try { + session = config.onSessionCut({ + cutSession: session, + replayer, + originalEvents: events, + }); + } catch (e) { + warn(config, 'Custom Event Handler error: ', e); + } + // TODO handle node mutations (hard to do) // TODO handle sequential plugin - // TODO handle custom event return session; } @@ -375,3 +416,9 @@ function filterAdoptedStyleData( }); return events; } + +function warn(config: CutterConfig, ...args: unknown[]) { + const logger = config.replayerConfig?.logger || console; + if (config.replayerConfig?.showWarning || config.replayerConfig?.showDebug) + logger.warn(args); +} From 1810f48c15572aaa69b33af7b925ab58fe7fde9c Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 20 Feb 2023 16:04:04 +1100 Subject: [PATCH 35/52] enable to build snapshot for iframes --- packages/rrweb-cutter/src/cut-session.ts | 156 +++++++++++++---------- 1 file changed, 92 insertions(+), 64 deletions(-) diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index bda357dfad..3d99bb12d8 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -7,12 +7,12 @@ import { styleSheetRuleData, } from '@rrweb/types'; import { - RRElement, - RRNode, RRCanvasElement, RRStyleElement, RRMediaElement, + RRIFrameElement, } from 'rrdom'; +import type { IRRNode, RRElement } from 'rrdom'; import { IncrementalSource, EventType, SyncReplayer } from 'rrweb'; import { playerConfig } from 'rrweb/typings/types'; import cloneDeep from 'lodash.clonedeep'; @@ -111,9 +111,9 @@ export function cutSession( .slice(index + 1) .filter((e) => e.timestamp <= nextCutTimestamp), replayer, + config, currentTimestamp, nextCutTimestamp, - config, ); results.push(session); eventsCache = session.events; @@ -159,9 +159,9 @@ function wrapCutSession( function cutEvents( events: eventWithTime[], replayer: SyncReplayer, + config: CutterConfig, currentTimestamp: number, cutTimestamp: number, - config: CutterConfig, ) { let result: eventWithTime[] = []; if (replayer.latestMetaEvent) { @@ -171,68 +171,95 @@ function cutEvents( } const fullsnapshotDelay = 1, IncrementalEventDelay = 2; + + const iframeSnapshots: eventWithTime[] = []; + const onSerialize = (n: IRRNode) => { + const timestamp = currentTimestamp + fullsnapshotDelay; + if (n.RRNodeType !== NodeType.Element) return; + const rrElement = n as RRElement; + rrElement.inputData && + result.push({ + type: EventType.IncrementalSnapshot, + data: rrElement.inputData, + timestamp, + }); + rrElement.scrollData && + result.push({ + type: EventType.IncrementalSnapshot, + data: rrElement.scrollData, + timestamp, + }); + if (rrElement instanceof RRCanvasElement) + rrElement.canvasMutations.forEach((canvasData) => + result.push({ + type: EventType.IncrementalSnapshot, + data: canvasData.mutation, + timestamp, + }), + ); + else if (rrElement instanceof RRStyleElement) + rrElement.rules.forEach((styleRule) => + result.push({ + type: EventType.IncrementalSnapshot, + data: styleRule, + timestamp, + }), + ); + else if (rrElement instanceof RRMediaElement) { + (rrElement.volume !== undefined || rrElement.muted !== undefined) && + result.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MediaInteraction, + type: MediaInteractions.VolumeChange, + volume: rrElement.volume, + muted: rrElement.muted, + id: replayer.getMirror().getId(n), + }, + timestamp, + }); + rrElement.playbackRate !== undefined && + result.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MediaInteraction, + type: MediaInteractions.RateChange, + playbackRate: rrElement.playbackRate, + id: replayer.getMirror().getId(n), + }, + timestamp, + }); + } else if (rrElement instanceof RRIFrameElement) { + if (!rrElement.contentDocument) return; + const iframeSnapshot = snapshot(rrElement.contentDocument, { + mirror: replayer.getMirror(), + onSerialize, + }); + if (!iframeSnapshot) return; + iframeSnapshots.push({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + adds: [ + { + parentId: replayer.getMirror().getId(rrElement), + nextId: null, + node: iframeSnapshot, + }, + ], + removes: [], + texts: [], + attributes: [], + isAttachIframe: true, + }, + timestamp, + }); + } + }; + let fullSnapshot = snapshot(replayer.virtualDom, { mirror: replayer.getMirror(), - onSerialize: (n: RRNode) => { - const timestamp = currentTimestamp + fullsnapshotDelay; - if (n.RRNodeType === NodeType.Element) { - const rrElement = n as RRElement; - rrElement.inputData && - result.push({ - type: EventType.IncrementalSnapshot, - data: rrElement.inputData, - timestamp, - }); - rrElement.scrollData && - result.push({ - type: EventType.IncrementalSnapshot, - data: rrElement.scrollData, - timestamp, - }); - if (rrElement instanceof RRCanvasElement) - rrElement.canvasMutations.forEach((canvasData) => - result.push({ - type: EventType.IncrementalSnapshot, - data: canvasData.mutation, - timestamp, - }), - ); - else if (rrElement instanceof RRStyleElement) - rrElement.rules.forEach((styleRule) => - result.push({ - type: EventType.IncrementalSnapshot, - data: styleRule, - timestamp, - }), - ); - else if (rrElement instanceof RRMediaElement) { - (rrElement.volume !== undefined || rrElement.muted !== undefined) && - result.push({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.MediaInteraction, - type: MediaInteractions.VolumeChange, - volume: rrElement.volume, - muted: rrElement.muted, - id: replayer.getMirror().getId(n), - }, - timestamp, - }); - rrElement.playbackRate !== undefined && - result.push({ - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.MediaInteraction, - type: MediaInteractions.RateChange, - playbackRate: rrElement.playbackRate, - id: replayer.getMirror().getId(n), - }, - timestamp, - }); - } - } - // TODO handle iframe - }, + onSerialize, }); if (!fullSnapshot) { console.warn( @@ -257,6 +284,7 @@ function cutEvents( timestamp: currentTimestamp + fullsnapshotDelay, }; result.push(fullSnapshotEvent); + result = result.concat(iframeSnapshots); result = result.concat( replayer.unhandledEvents.map((e) => ({ ...e, From 3335897229fff71d9c0a8e25de9a8aeacd522a6c Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 20 Feb 2023 16:33:52 +1100 Subject: [PATCH 36/52] refactor unit tests --- ...st.ts.snap => prune-branches.test.ts.snap} | 92 +-------- ...ion-cutter.test.ts => cut-session.test.ts} | 189 ++---------------- .../rrweb-cutter/test/prune-branches.test.ts | 173 ++++++++++++++++ packages/rrweb/src/replay/sync-replayer.ts | 2 - 4 files changed, 191 insertions(+), 265 deletions(-) rename packages/rrweb-cutter/test/__snapshots__/{session-cutter.test.ts.snap => prune-branches.test.ts.snap} (70%) rename packages/rrweb-cutter/test/{session-cutter.test.ts => cut-session.test.ts} (51%) create mode 100644 packages/rrweb-cutter/test/prune-branches.test.ts diff --git a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap similarity index 70% rename from packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap rename to packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap index d113052141..f9bd371400 100644 --- a/packages/rrweb-cutter/test/__snapshots__/session-cutter.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`pruneBranches should cut branches that doesn't include id: pruned all but 14 1`] = ` +exports[`prune branches should cut branches that doesn't include id: pruned all but 14 1`] = ` "1 RRDocument -1 RRDocumentType 2 HTML @@ -10,7 +10,7 @@ exports[`pruneBranches should cut branches that doesn't include id: pruned all b " `; -exports[`pruneBranches should keep branches where target child nodes was, and gets moved to 1`] = ` +exports[`prune branches should keep branches where target child nodes was, and gets moved to 1`] = ` "[ { \\"type\\": 0, @@ -127,7 +127,7 @@ exports[`pruneBranches should keep branches where target child nodes was, and ge ]" `; -exports[`pruneBranches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` +exports[`prune branches should remove branches based on child nodes that came in after fullsnapshot 1`] = ` "[ { \\"type\\": 0, @@ -218,7 +218,7 @@ exports[`pruneBranches should remove branches based on child nodes that came in ]" `; -exports[`pruneBranches should remove branches based on nodes that came in after fullsnapshot 1`] = ` +exports[`prune branches should remove branches based on nodes that came in after fullsnapshot 1`] = ` "[ { \\"type\\": 0, @@ -301,7 +301,7 @@ exports[`pruneBranches should remove branches based on nodes that came in after ]" `; -exports[`pruneBranches should remove mutations that don't include ids 1`] = ` +exports[`prune branches should remove mutations that don't include ids 1`] = ` Object { "data": Object { "adds": Array [ @@ -336,85 +336,3 @@ Object { "type": 3, } `; - -exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 1000ms 1`] = ` -"1 RRDocument - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 34 DIV - 35 RRText text=\\"1\\" - 33 DIV - 36 RRText text=\\"2\\" - 32 DIV - 37 RRText text=\\"3\\" - 31 DIV - 38 RRText text=\\"4\\" - 29 DIV - 30 RRText text=\\"5\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 2000ms 1`] = ` -"1 RRDocument - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 29 DIV - 30 RRText text=\\"5\\" - 31 DIV - 38 RRText text=\\"4\\" - 32 DIV - 37 RRText text=\\"3\\" - 33 DIV - 36 RRText text=\\"2\\" - 34 DIV - 35 RRText text=\\"1\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter Cut the session events from several time points should cut the simplest mutation events: screenshot at 3000ms 1`] = ` -"1 RRDocument - 2 HTML - 3 HEAD - 4 META charset=\\"utf-8\\" - 5 RRText text=\\" \\\\n \\" - 6 BODY - 16 RRText text=\\"\\\\n \\" - 17 DIV id=\\"container\\" - 19 RRText text=\\"\\\\n \\\\n \\" -" -`; - -exports[`session cutter should cut events with inline styles: screenshot at 1000ms 1`] = ` -"1 RRDocument - 2 HTML - 3 HEAD - 4 RRText text=\\"\\\\n \\" - 5 META charset=\\"utf-8\\" - 6 RRText text=\\"\\\\n \\" - 7 STYLE - -1 RRText text=\\"#root { background: yellow; width: 10px; height: 10px; }\\" - 8 RRText text=\\"\\\\n \\" - 9 STYLE - -1 RRText text=\\".block { width: 20px; height: 20px; background: red; }\\" - 10 RRText text=\\"\\\\n \\" - 11 RRText text=\\"\\\\n \\" - 12 BODY - 13 RRText text=\\"\\\\n \\" - 14 DIV id=\\"root\\" - 15 RRText text=\\" \\\\n \\" - 16 RRText text=\\"\\\\n \\" - 17 DIV class=\\"block\\" -" -`; diff --git a/packages/rrweb-cutter/test/session-cutter.test.ts b/packages/rrweb-cutter/test/cut-session.test.ts similarity index 51% rename from packages/rrweb-cutter/test/session-cutter.test.ts rename to packages/rrweb-cutter/test/cut-session.test.ts index 7cb6ad84d2..53aa8284e9 100644 --- a/packages/rrweb-cutter/test/session-cutter.test.ts +++ b/packages/rrweb-cutter/test/cut-session.test.ts @@ -3,22 +3,21 @@ */ import path from 'path'; import fs from 'fs'; -import { createMirror, snapshot, NodeType } from 'rrweb-snapshot'; +import { createMirror, snapshot } from 'rrweb-snapshot'; import { EventType } from 'rrweb'; import { SyncReplayer } from 'rrweb'; import type { eventWithTime } from '@rrweb/types'; import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; -import { sessionCut, getValidSortedPoints, pruneBranches } from '../src'; +import { cutSession, getValidSortedPoints } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; -import { assertSnapshot } from 'rrweb/test/utils'; -describe('session cutter', () => { +describe('cut session', () => { it('should return the same events if the events length is too short', () => { const events1: eventWithTime[] = []; const config = { points: [10] }; - expect(sessionCut(events1, config)).toEqual([events1]); + expect(cutSession(events1, config)).toEqual([events1]); const events2: eventWithTime[] = [ { @@ -27,7 +26,7 @@ describe('session cutter', () => { timestamp: 1, } as eventWithTime, ]; - expect(sessionCut(events2, config)).toEqual([events2]); + expect(cutSession(events2, config)).toEqual([events2]); }); it('should return the same events if the points length is 0', () => { @@ -44,7 +43,7 @@ describe('session cutter', () => { } as eventWithTime, ]; const config = { points: [] }; - expect(sessionCut(events, config)).toEqual([events]); + expect(cutSession(events, config)).toEqual([events]); }); it('should sort and validate cutting points array', () => { @@ -77,18 +76,18 @@ describe('session cutter', () => { describe('Cut the session events from several time points', () => { it('should cut the simplest mutation events', () => { const events = mutationEvents as eventWithTime[]; - const result = sessionCut(events, { points: [1000, 2000] }); + const result = cutSession(events, { points: [1000, 2000] }); expect(result).toHaveLength(3); // all events before 1000ms - const sessionBefore1s = result[0]; + const sessionBefore1s = result[0].events; const cutPoint1Length = 5; expect(sessionBefore1s).toHaveLength(cutPoint1Length); // These events are directly sliced from the original events. expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); // all events between 1000ms and 2000ms - const sessionBetween1s2s = result[1]; + const sessionBetween1s2s = result[1].events; expect(sessionBetween1s2s).toHaveLength(3); expect(sessionBetween1s2s[0].type).toEqual(EventType.Meta); expect(sessionBetween1s2s[1].type).toEqual(EventType.FullSnapshot); @@ -101,7 +100,7 @@ describe('session cutter', () => { ).toMatchSnapshot('screenshot at 1000ms'); // all events after 2000ms - const sessionAfter2s = result[2]; + const sessionAfter2s = result[2].events; expect(sessionAfter2s).toHaveLength(3); expect(sessionAfter2s[0].type).toEqual(EventType.Meta); expect(sessionAfter2s[1].type).toEqual(EventType.FullSnapshot); @@ -124,17 +123,17 @@ describe('session cutter', () => { it('should cut events with inline styles', () => { const events = inlineStyleEvents() as eventWithTime[]; - const result = sessionCut(events, { points: [1000] }); + const result = cutSession(events, { points: [1000] }); expect(result).toHaveLength(2); // all events before 1000ms - const sessionBefore1s = result[0]; + const sessionBefore1s = result[0].events; const cutPoint1Length = 5; expect(sessionBefore1s).toHaveLength(cutPoint1Length); // These events are directly sliced from the original events. expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); // all events after 1000ms - const sessionAfter1s = result[1]; + const sessionAfter1s = result[1].events; expect(sessionAfter1s).toHaveLength(3); const replayer = new SyncReplayer(sessionAfter1s.slice(0, 2)); // only play meta and full snapshot events replayer.play(); @@ -145,168 +144,6 @@ describe('session cutter', () => { }); }); -describe('pruneBranches', () => { - it("should cut branches that doesn't include id", () => { - const events = inlineStyleEvents() as eventWithTime[]; - const result = pruneBranches(events, { keep: [14] }); - expect(result).toHaveLength(5); - const replayer = new SyncReplayer(result); - replayer.play(); - - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot('pruned all but 14'); - }); - - it("should remove mutations that don't include ids", () => { - const mutationEvent = { - type: EventType.IncrementalSnapshot, - data: { - source: 0, - texts: [ - { - id: 15, - value: 'Kept', - }, - { - id: 1001, - value: 'Cut', - }, - ], - attributes: [ - { - id: 14, - attributes: { - 'data-attr': 'Kept', - }, - }, - { - id: 1002, - attributes: { - 'data-attr': 'Cut', - }, - }, - ], - removes: [ - { - parentId: 14, - id: 15, - }, - { - parentId: 1002, - id: 1003, - }, - ], - adds: [ - { - parentId: 14, - nextId: null, - node: {}, - }, - { - parentId: 1001, - nextId: null, - node: {}, - }, - ], - }, - }; - const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; - const result = pruneBranches(events, { keep: [14] }); - expect(result[result.length - 1]).toMatchSnapshot(); - }); - - it('should remove branches based on nodes that came in after fullsnapshot', () => { - const mutationEvent = { - type: EventType.IncrementalSnapshot, - data: { - source: 0, - texts: [], - attributes: [], - removes: [], - adds: [ - { - parentId: 14, - nextId: null, - node: { - id: 99, - type: NodeType.Element, - tagName: 'canvas', - attributes: {}, - childNodes: [], - }, - }, - ], - }, - }; - const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; - const result = pruneBranches(events, { keep: [99] }); - assertSnapshot(result); - }); - it('should remove branches based on child nodes that came in after fullsnapshot', () => { - const mutationEvent = { - type: EventType.IncrementalSnapshot, - data: { - source: 0, - texts: [], - attributes: [], - removes: [], - adds: [ - { - parentId: 14, - nextId: null, - node: { - id: 98, - type: NodeType.Element, - tagName: 'main', - attributes: {}, - childNodes: [ - { - id: 99, - type: NodeType.Element, - tagName: 'canvas', - attributes: {}, - childNodes: [], - }, - ], - }, - }, - ], - }, - }; - const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; - const result = pruneBranches(events, { keep: [99] }); - assertSnapshot(result); - }); - - it('should keep branches where target child nodes was, and gets moved to', () => { - const mutationEvent = { - type: EventType.IncrementalSnapshot, - data: { - source: 0, - texts: [], - attributes: [], - removes: [ - { - parentId: 14, - id: 15, - }, - ], - adds: [ - { - parentId: 5, - nextId: null, - node: { type: 3, textContent: ' \n ', id: 15 }, - }, - ], - }, - }; - const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; - const result = pruneBranches(events, { keep: [15] }); - assertSnapshot(result); - }); -}); - function getHtml(fileName: string) { const filePath = path.resolve(__dirname, `./html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); diff --git a/packages/rrweb-cutter/test/prune-branches.test.ts b/packages/rrweb-cutter/test/prune-branches.test.ts new file mode 100644 index 0000000000..af2c939a51 --- /dev/null +++ b/packages/rrweb-cutter/test/prune-branches.test.ts @@ -0,0 +1,173 @@ +/** + * @jest-environment jsdom + */ +import { NodeType } from 'rrweb-snapshot'; +import { EventType } from 'rrweb'; +import { SyncReplayer } from 'rrweb'; +import type { eventWithTime } from '@rrweb/types'; +import { printRRDom } from 'rrdom'; +import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; +import { pruneBranches } from '../src'; +import { assertSnapshot } from 'rrweb/test/utils'; + +describe('prune branches', () => { + it("should cut branches that doesn't include id", () => { + const events = inlineStyleEvents() as eventWithTime[]; + const result = pruneBranches(events, { keep: [14] }); + expect(result).toHaveLength(5); + const replayer = new SyncReplayer(result); + replayer.play(); + + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('pruned all but 14'); + }); + + it("should remove mutations that don't include ids", () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [ + { + id: 15, + value: 'Kept', + }, + { + id: 1001, + value: 'Cut', + }, + ], + attributes: [ + { + id: 14, + attributes: { + 'data-attr': 'Kept', + }, + }, + { + id: 1002, + attributes: { + 'data-attr': 'Cut', + }, + }, + ], + removes: [ + { + parentId: 14, + id: 15, + }, + { + parentId: 1002, + id: 1003, + }, + ], + adds: [ + { + parentId: 14, + nextId: null, + node: {}, + }, + { + parentId: 1001, + nextId: null, + node: {}, + }, + ], + }, + }; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [14] }); + expect(result[result.length - 1]).toMatchSnapshot(); + }); + + it('should remove branches based on nodes that came in after fullsnapshot', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 14, + nextId: null, + node: { + id: 99, + type: NodeType.Element, + tagName: 'canvas', + attributes: {}, + childNodes: [], + }, + }, + ], + }, + }; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [99] }); + assertSnapshot(result); + }); + it('should remove branches based on child nodes that came in after fullsnapshot', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 14, + nextId: null, + node: { + id: 98, + type: NodeType.Element, + tagName: 'main', + attributes: {}, + childNodes: [ + { + id: 99, + type: NodeType.Element, + tagName: 'canvas', + attributes: {}, + childNodes: [], + }, + ], + }, + }, + ], + }, + }; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [99] }); + assertSnapshot(result); + }); + + it('should keep branches where target child nodes was, and gets moved to', () => { + const mutationEvent = { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [ + { + parentId: 14, + id: 15, + }, + ], + adds: [ + { + parentId: 5, + nextId: null, + node: { type: 3, textContent: ' \n ', id: 15 }, + }, + ], + }, + }; + const events = [...inlineStyleEvents(), mutationEvent] as eventWithTime[]; + const result = pruneBranches(events, { keep: [15] }); + assertSnapshot(result); + }); +}); diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index 1477a6ea21..b84159424e 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -231,8 +231,6 @@ export class SyncReplayer { const baseTime = this.events[0].timestamp; for (let i = this.events.length - 1; i >= 0; i--) { const event = this.events[i]; - const castFn = this.getCastFn(event); - castFn(); this.currentTime = event.timestamp - baseTime; if ( castEventCallback?.({ From ca3bec81494d8682106849c63d69491b313fec6d Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 21 Feb 2023 17:46:46 +1100 Subject: [PATCH 37/52] add support for sequential ID plugin --- packages/rrweb-cutter/src/cut-session.ts | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index 3d99bb12d8..98e7aa4f85 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -19,7 +19,13 @@ import cloneDeep from 'lodash.clonedeep'; import snapshot from './snapshot'; export type CutterConfig = { points: number[]; + // config for the Sync Replayer replayerConfig?: Partial; + /** + * If true, the sequentialId of the events will be updated. + * If a string, the sequentialId will be updated with the string as the key. + */ + updateSequentialId?: boolean | string; onSessionCut?: (context: { replayer: SyncReplayer; cutSession: SessionCut; @@ -47,14 +53,18 @@ export function cutSession( if (events.length < 2) return []; const { points } = config; if (!points || points.length == 0) - return [wrapCutSession(events, 0, events[events.length - 1].timestamp)]; + return [ + wrapCutSession(events, config, 0, events[events.length - 1].timestamp), + ]; events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); const totalTime = events[events.length - 1].timestamp - events[0].timestamp; const validSortedPoints = getValidSortedPoints(points, totalTime); if (validSortedPoints.length < 1) - return [wrapCutSession(events, 0, events[events.length - 1].timestamp)]; + return [ + wrapCutSession(events, config, 0, events[events.length - 1].timestamp), + ]; const results: SessionCut[] = []; const replayer = new SyncReplayer(events, { ...config.replayerConfig, @@ -69,7 +79,12 @@ export function cutSession( if (event.timestamp <= cutPoint && index + 1 < events.length) { if (results.length === 0) { results.push( - wrapCutSession(events.slice(0, index + 1), 0, cutPoint - baseTime), + wrapCutSession( + events.slice(0, index + 1), + config, + 0, + cutPoint - baseTime, + ), ); } const nextEvent = events[index + 1]; @@ -101,7 +116,12 @@ export function cutSession( : e.timestamp, })); results.push( - wrapCutSession(newEvents, currentTimestamp, nextCutTimestamp), + wrapCutSession( + newEvents, + config, + currentTimestamp, + nextCutTimestamp, + ), ); continue; } @@ -138,11 +158,26 @@ export function getValidSortedPoints(points: number[], totalTime: number) { function wrapCutSession( events: eventWithTime[], + config: CutterConfig, startTimestamp: number, endTimestamp: number, ): SessionCut { + let clonedEvents = cloneDeep(events); + if (config.updateSequentialId) { + const key = + typeof config.updateSequentialId === 'string' + ? config.updateSequentialId + : '_sid'; + let sequentialId = 0; + clonedEvents = events.map((e) => { + Object.assign(e, { + [key]: ++sequentialId, + }); + return e; + }); + } return { - events: cloneDeep(events), + events: clonedEvents, startTimestamp, endTimestamp, }; @@ -347,6 +382,7 @@ function cutEvents( result = result.concat(events); let session = wrapCutSession( result, + config, currentTimestamp + IncrementalEventDelay, cutTimestamp, ); @@ -360,9 +396,6 @@ function cutEvents( } catch (e) { warn(config, 'Custom Event Handler error: ', e); } - - // TODO handle node mutations (hard to do) - // TODO handle sequential plugin return session; } From 6318e26314134586bce331e2114cc002f4caba2f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 21 Feb 2023 23:07:31 +1100 Subject: [PATCH 38/52] update test case and fix bugs --- packages/rrweb-cutter/src/cut-session.ts | 153 +++++++------ .../__snapshots__/cut-session.test.ts.snap | 83 +++++++ .../rrweb-cutter/test/cut-session.test.ts | 215 ++++++++++++------ 3 files changed, 307 insertions(+), 144 deletions(-) create mode 100644 packages/rrweb-cutter/test/__snapshots__/cut-session.test.ts.snap diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index 98e7aa4f85..4eb3163f5c 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -43,105 +43,111 @@ export type SessionCut = { events: eventWithTime[]; startTimestamp: number; endTimestamp: number; + startTime: number; + endTime: number; }; export function cutSession( events: eventWithTime[], config: CutterConfig, ): SessionCut[] { + const baseTimestamp = events[0]?.timestamp || 0; + const defaultResult = [ + wrapCutSession( + events, + config, + baseTimestamp, + events[events.length - 1]?.timestamp || 0, + baseTimestamp, + ), + ]; // Events length is too short so that cutting process is not needed. - if (events.length < 2) return []; + if (events.length < 2) return defaultResult; + const { points } = config; - if (!points || points.length == 0) - return [ - wrapCutSession(events, config, 0, events[events.length - 1].timestamp), - ]; + if (!points || points.length == 0) return defaultResult; events = events.sort((a1, a2) => a1.timestamp - a2.timestamp); const totalTime = events[events.length - 1].timestamp - events[0].timestamp; const validSortedPoints = getValidSortedPoints(points, totalTime); - if (validSortedPoints.length < 1) - return [ - wrapCutSession(events, config, 0, events[events.length - 1].timestamp), - ]; + if (validSortedPoints.length < 1) return defaultResult; + const validSortedTimestamp = validSortedPoints.map( + (point) => baseTimestamp + point, + ); + const results: SessionCut[] = []; const replayer = new SyncReplayer(events, { ...config.replayerConfig, }); let cutPointIndex = 0; - const baseTime = events[0].timestamp; - const validSortedTimestamp = validSortedPoints.map( - (point) => baseTime + point, - ); - replayer.play(({ index, event }) => { - const cutPoint = validSortedPoints[cutPointIndex]; - if (event.timestamp <= cutPoint && index + 1 < events.length) { + replayer.play(({ index }) => { + if (index + 1 >= events.length) return false; + + const nextEvent = events[index + 1]; + + let currentTimestamp = validSortedTimestamp[cutPointIndex]; + let eventsCache: eventWithTime[] | null = null; + /** + * This loop is for the situation that cutting points are in the middle gap between two events. + * These cut points have the same cut events so there is no need to generate them for each cut point. + * We can cache the them and use them for the next cut point. + */ + while ( + cutPointIndex < validSortedTimestamp.length && + // If the next event exceed the cut point, we need to cut the events with the current replayer status. + nextEvent.timestamp > validSortedTimestamp[cutPointIndex] + ) { if (results.length === 0) { results.push( wrapCutSession( events.slice(0, index + 1), config, - 0, - cutPoint - baseTime, + baseTimestamp, + currentTimestamp, + baseTimestamp, ), ); } - const nextEvent = events[index + 1]; - - let currentTimestamp = cutPoint; - let eventsCache: eventWithTime[] | null = null; - /** - * This loop is for the situation that cutting points are in the middle gap between two events. - * These cut points have the same cut events so there is no need to generate them for each cut point. - * We can cache the them and use them for the next cut point. - */ - while ( - cutPointIndex < validSortedTimestamp.length && - // If the next event exceed the cut point, we need to cut the events with the current replayer status. - nextEvent.timestamp > validSortedTimestamp[cutPointIndex] - ) { - cutPointIndex++; - const nextCutTimestamp = - cutPointIndex < validSortedPoints.length - ? validSortedTimestamp[cutPointIndex] - : events[events.length - 1].timestamp; - if (eventsCache !== null) { - const timeDiff = currentTimestamp - eventsCache[0].timestamp; - const newEvents = eventsCache.map((e) => ({ - ...e, - timestamp: - e.timestamp < currentTimestamp - ? e.timestamp + timeDiff - : e.timestamp, - })); - results.push( - wrapCutSession( - newEvents, - config, - currentTimestamp, - nextCutTimestamp, - ), - ); - continue; - } - - const session = cutEvents( - events - .slice(index + 1) - .filter((e) => e.timestamp <= nextCutTimestamp), - replayer, - config, - currentTimestamp, - nextCutTimestamp, + cutPointIndex++; + const nextCutTimestamp = + cutPointIndex < validSortedTimestamp.length + ? validSortedTimestamp[cutPointIndex] + : events[events.length - 1].timestamp; + if (eventsCache !== null) { + const timeDiff = currentTimestamp - eventsCache[0].timestamp; + const newEvents = eventsCache.map((e) => ({ + ...e, + timestamp: + e.timestamp < currentTimestamp + ? e.timestamp + timeDiff + : e.timestamp, + })); + results.push( + wrapCutSession( + newEvents, + config, + currentTimestamp, + nextCutTimestamp, + baseTimestamp, + ), ); - results.push(session); - eventsCache = session.events; - currentTimestamp = nextCutTimestamp; + continue; } - return cutPointIndex < validSortedTimestamp.length; + + const session = cutEvents( + events.slice(index + 1).filter((e) => e.timestamp <= nextCutTimestamp), + replayer, + config, + currentTimestamp, + nextCutTimestamp, + baseTimestamp, + ); + results.push(session); + eventsCache = session.events; + currentTimestamp = nextCutTimestamp; } - return false; + return cutPointIndex < validSortedTimestamp.length; }); return results; } @@ -161,6 +167,7 @@ function wrapCutSession( config: CutterConfig, startTimestamp: number, endTimestamp: number, + baseTimestamp: number, ): SessionCut { let clonedEvents = cloneDeep(events); if (config.updateSequentialId) { @@ -180,6 +187,8 @@ function wrapCutSession( events: clonedEvents, startTimestamp, endTimestamp, + startTime: startTimestamp - baseTimestamp, + endTime: endTimestamp - baseTimestamp, }; } @@ -197,6 +206,7 @@ function cutEvents( config: CutterConfig, currentTimestamp: number, cutTimestamp: number, + baseTimestamp: number, ) { let result: eventWithTime[] = []; if (replayer.latestMetaEvent) { @@ -383,8 +393,9 @@ function cutEvents( let session = wrapCutSession( result, config, - currentTimestamp + IncrementalEventDelay, + currentTimestamp, cutTimestamp, + baseTimestamp, ); if (config.onSessionCut) try { diff --git a/packages/rrweb-cutter/test/__snapshots__/cut-session.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/cut-session.test.ts.snap new file mode 100644 index 0000000000..9dbfdd9ecb --- /dev/null +++ b/packages/rrweb-cutter/test/__snapshots__/cut-session.test.ts.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`cut session should cut events with inline styles: screenshot at 1000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 RRText text=\\"\\\\n \\" + 5 META charset=\\"utf-8\\" + 6 RRText text=\\"\\\\n \\" + 7 STYLE + -1 RRText text=\\"#root { background: yellow; width: 10px; height: 10px; }\\" + 8 RRText text=\\"\\\\n \\" + 9 STYLE + -1 RRText text=\\".block { width: 20px; height: 20px; background: red; }\\" + 10 RRText text=\\"\\\\n \\" + 11 RRText text=\\"\\\\n \\" + 12 BODY + 13 RRText text=\\"\\\\n \\" + 14 DIV id=\\"root\\" + 15 RRText text=\\" \\\\n \\" + 16 RRText text=\\"\\\\n \\" + 17 DIV class=\\"block\\" +" +`; + +exports[`cut session should cut the simplest mutation events: screenshot at 1000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 34 DIV + 35 RRText text=\\"1\\" + 33 DIV + 36 RRText text=\\"2\\" + 32 DIV + 37 RRText text=\\"3\\" + 31 DIV + 38 RRText text=\\"4\\" + 29 DIV + 30 RRText text=\\"5\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`cut session should cut the simplest mutation events: screenshot at 2000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 29 DIV + 30 RRText text=\\"5\\" + 31 DIV + 38 RRText text=\\"4\\" + 32 DIV + 37 RRText text=\\"3\\" + 33 DIV + 36 RRText text=\\"2\\" + 34 DIV + 35 RRText text=\\"1\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; + +exports[`cut session should cut the simplest mutation events: screenshot at 3000ms 1`] = ` +"1 RRDocument + 2 HTML + 3 HEAD + 4 META charset=\\"utf-8\\" + 5 RRText text=\\" \\\\n \\" + 6 BODY + 16 RRText text=\\"\\\\n \\" + 17 DIV id=\\"container\\" + 19 RRText text=\\"\\\\n \\\\n \\" +" +`; diff --git a/packages/rrweb-cutter/test/cut-session.test.ts b/packages/rrweb-cutter/test/cut-session.test.ts index 53aa8284e9..2a0acf08f9 100644 --- a/packages/rrweb-cutter/test/cut-session.test.ts +++ b/packages/rrweb-cutter/test/cut-session.test.ts @@ -17,16 +17,32 @@ describe('cut session', () => { it('should return the same events if the events length is too short', () => { const events1: eventWithTime[] = []; const config = { points: [10] }; - expect(cutSession(events1, config)).toEqual([events1]); + expect(cutSession(events1, config)).toEqual([ + { + events: events1, + startTimestamp: 0, + endTimestamp: 0, + startTime: 0, + endTime: 0, + }, + ]); const events2: eventWithTime[] = [ { type: EventType.Load, data: {}, - timestamp: 1, + timestamp: Date.now(), } as eventWithTime, ]; - expect(cutSession(events2, config)).toEqual([events2]); + expect(cutSession(events2, config)).toEqual([ + { + events: events2, + startTimestamp: events2[0].timestamp, + endTimestamp: events2[0].timestamp, + startTime: 0, + endTime: 0, + }, + ]); }); it('should return the same events if the points length is 0', () => { @@ -34,16 +50,24 @@ describe('cut session', () => { { type: EventType.Load, data: {}, - timestamp: 1, + timestamp: Date.now(), } as eventWithTime, { type: EventType.Meta, data: {}, - timestamp: 2, + timestamp: Date.now() + 100, } as eventWithTime, ]; const config = { points: [] }; - expect(cutSession(events, config)).toEqual([events]); + expect(cutSession(events, config)).toEqual([ + { + events, + startTimestamp: events[0].timestamp, + endTimestamp: events[1].timestamp, + startTime: 0, + endTime: events[1].timestamp - events[0].timestamp, + }, + ]); }); it('should sort and validate cutting points array', () => { @@ -55,87 +79,114 @@ describe('cut session', () => { expect(getValidSortedPoints(inputPoints, 300)).toEqual([10, 100, 250.5]); }); - describe('Build full snapshot events from RRDom', () => { - it("should build full snapshot events from RRDom's mirror: main.html", () => { - document.write(getHtml('main.html')); - const rrdom = new RRDocument(); - const mirror = createMirror(); - // the full snapshot that is built on jsdom - const originalSnapshot = snapshot(document, { mirror }); - - // Create a RRDom according to the jsdom (real dom). - buildFromDom(document, mirror, rrdom); - - const newFullSnapshot = RRDomSnapshot(rrdom, { - mirror: rrdom.mirror, - }); - expect(newFullSnapshot).toEqual(originalSnapshot); - }); - }); - - describe('Cut the session events from several time points', () => { - it('should cut the simplest mutation events', () => { - const events = mutationEvents as eventWithTime[]; - const result = cutSession(events, { points: [1000, 2000] }); - expect(result).toHaveLength(3); + it('should cut the simplest mutation events', () => { + const events = mutationEvents as eventWithTime[]; + const cutTime1 = 1000, + cutTime2 = 2000; + const result = cutSession(events, { points: [cutTime1, cutTime2] }); + expect(result).toHaveLength(3); - // all events before 1000ms - const sessionBefore1s = result[0].events; - const cutPoint1Length = 5; - expect(sessionBefore1s).toHaveLength(cutPoint1Length); - // These events are directly sliced from the original events. - expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); + // cut session before 1000ms + const sessionBefore1s = result[0]; + expect(sessionBefore1s.startTimestamp).toEqual(events[0].timestamp); + expect(sessionBefore1s.endTimestamp).toEqual( + events[0].timestamp + cutTime1, + ); + expect(sessionBefore1s.startTime).toEqual(0); + expect(sessionBefore1s.endTime).toEqual(cutTime1); + const cutPoint1Length = 5; + expect(sessionBefore1s.events).toHaveLength(cutPoint1Length); + // These events are directly sliced from the original events. + expect(sessionBefore1s.events).toEqual(events.slice(0, cutPoint1Length)); - // all events between 1000ms and 2000ms - const sessionBetween1s2s = result[1].events; - expect(sessionBetween1s2s).toHaveLength(3); - expect(sessionBetween1s2s[0].type).toEqual(EventType.Meta); - expect(sessionBetween1s2s[1].type).toEqual(EventType.FullSnapshot); - expect(sessionBetween1s2s[2].type).toEqual(EventType.IncrementalSnapshot); - let replayer = new SyncReplayer(sessionBetween1s2s.slice(0, 2)); // only play meta and full snapshot events - replayer.play(); - // screenshot at 1000ms - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot('screenshot at 1000ms'); + // all events between 1000ms and 2000ms + const sessionBetween1s2s = result[1]; + expect(sessionBetween1s2s.startTimestamp).toEqual( + events[0].timestamp + cutTime1, + ); + expect(sessionBetween1s2s.endTimestamp).toEqual( + events[0].timestamp + cutTime2, + ); + expect(sessionBetween1s2s.startTime).toEqual(cutTime1); + expect(sessionBetween1s2s.endTime).toEqual(cutTime2); + expect(sessionBetween1s2s.events).toHaveLength(3); + expect(sessionBetween1s2s.events[0].type).toEqual(EventType.Meta); + expect(sessionBetween1s2s.events[1].type).toEqual(EventType.FullSnapshot); + expect(sessionBetween1s2s.events[2].type).toEqual( + EventType.IncrementalSnapshot, + ); + let replayer = new SyncReplayer(sessionBetween1s2s.events.slice(0, 2)); // only play meta and full snapshot events + replayer.play(); + // screenshot at 1000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 1000ms'); - // all events after 2000ms - const sessionAfter2s = result[2].events; - expect(sessionAfter2s).toHaveLength(3); - expect(sessionAfter2s[0].type).toEqual(EventType.Meta); - expect(sessionAfter2s[1].type).toEqual(EventType.FullSnapshot); - expect(sessionAfter2s[2].type).toEqual(EventType.IncrementalSnapshot); - replayer = new SyncReplayer(sessionAfter2s); - replayer.play(({ index }) => { - if (index === 1) - // full snapshot - // screen shot at 2000ms - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot('screenshot at 2000ms'); - }); - // screen shot at 3000ms - expect( - printRRDom(replayer.virtualDom, replayer.getMirror()), - ).toMatchSnapshot('screenshot at 3000ms'); + // all events after 2000ms + const sessionAfter2s = result[2]; + expect(sessionAfter2s.startTimestamp).toEqual( + events[0].timestamp + cutTime2, + ); + expect(sessionAfter2s.endTimestamp).toEqual( + events[events.length - 1].timestamp, + ); + expect(sessionAfter2s.startTime).toEqual(cutTime2); + expect(sessionAfter2s.endTime).toEqual( + events[events.length - 1].timestamp - events[0].timestamp, + ); + expect(sessionAfter2s.events).toHaveLength(3); + expect(sessionAfter2s.events[0].type).toEqual(EventType.Meta); + expect(sessionAfter2s.events[1].type).toEqual(EventType.FullSnapshot); + expect(sessionAfter2s.events[2].type).toEqual( + EventType.IncrementalSnapshot, + ); + replayer = new SyncReplayer(sessionAfter2s.events); + replayer.play(({ index }) => { + if (index === 1) + // full snapshot + // screen shot at 2000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 2000ms'); }); + // screen shot at 3000ms + expect( + printRRDom(replayer.virtualDom, replayer.getMirror()), + ).toMatchSnapshot('screenshot at 3000ms'); }); it('should cut events with inline styles', () => { const events = inlineStyleEvents() as eventWithTime[]; - const result = cutSession(events, { points: [1000] }); + const cutTime1 = 1000; + const result = cutSession(events, { points: [cutTime1] }); expect(result).toHaveLength(2); // all events before 1000ms - const sessionBefore1s = result[0].events; + const sessionBefore1s = result[0]; + expect(sessionBefore1s.startTimestamp).toEqual(events[0].timestamp); + expect(sessionBefore1s.endTimestamp).toEqual( + events[0].timestamp + cutTime1, + ); + expect(sessionBefore1s.startTime).toEqual(0); + expect(sessionBefore1s.endTime).toEqual(cutTime1); const cutPoint1Length = 5; - expect(sessionBefore1s).toHaveLength(cutPoint1Length); + expect(sessionBefore1s.events).toHaveLength(cutPoint1Length); // These events are directly sliced from the original events. - expect(sessionBefore1s).toEqual(events.slice(0, cutPoint1Length)); + expect(sessionBefore1s.events).toEqual(events.slice(0, cutPoint1Length)); // all events after 1000ms - const sessionAfter1s = result[1].events; - expect(sessionAfter1s).toHaveLength(3); - const replayer = new SyncReplayer(sessionAfter1s.slice(0, 2)); // only play meta and full snapshot events + const sessionAfter1s = result[1]; + expect(sessionAfter1s.startTimestamp).toEqual( + events[0].timestamp + cutTime1, + ); + expect(sessionAfter1s.endTimestamp).toEqual( + events[events.length - 1].timestamp, + ); + expect(sessionAfter1s.startTime).toEqual(cutTime1); + expect(sessionAfter1s.endTime).toEqual( + events[events.length - 1].timestamp - events[0].timestamp, + ); + expect(sessionAfter1s.events).toHaveLength(3); + const replayer = new SyncReplayer(sessionAfter1s.events.slice(0, 2)); // only play meta and full snapshot events replayer.play(); // screenshot at 1000ms expect( @@ -144,6 +195,24 @@ describe('cut session', () => { }); }); +describe('Build full snapshot events from RRDom', () => { + it("should build full snapshot events from RRDom's mirror: main.html", () => { + document.write(getHtml('main.html')); + const rrdom = new RRDocument(); + const mirror = createMirror(); + // the full snapshot that is built on jsdom + const originalSnapshot = snapshot(document, { mirror }); + + // Create a RRDom according to the jsdom (real dom). + buildFromDom(document, mirror, rrdom); + + const newFullSnapshot = RRDomSnapshot(rrdom, { + mirror: rrdom.mirror, + }); + expect(newFullSnapshot).toEqual(originalSnapshot); + }); +}); + function getHtml(fileName: string) { const filePath = path.resolve(__dirname, `./html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); From f24412712a3a8f38e5b1bba2d89a2e63f9781edc Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 21 Feb 2023 23:18:02 +1100 Subject: [PATCH 39/52] add change log --- .changeset/gorgeous-eyes-agree.md | 8 ++++++++ .changeset/popular-seals-buy.md | 5 +++++ 2 files changed, 13 insertions(+) create mode 100644 .changeset/gorgeous-eyes-agree.md create mode 100644 .changeset/popular-seals-buy.md diff --git a/.changeset/gorgeous-eyes-agree.md b/.changeset/gorgeous-eyes-agree.md new file mode 100644 index 0000000000..038ac9c540 --- /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..7a88ec5c55 --- /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. From 09881bb373399b6dd784e74994d90fc967b3eadc Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 10 Apr 2023 18:42:42 +1000 Subject: [PATCH 40/52] apply the change of https://github.com/rrweb-io/rrweb/pull/1145 to SyncReplayer --- packages/rrweb/src/replay/sync-replayer.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index b84159424e..8a01ecefe7 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -626,14 +626,21 @@ export class SyncReplayer { private applyMutation(d: mutationData) { const mirror = this.mirror; + d.removes = d.removes.filter((mutation) => { + // warn of absence from mirror before we start applying each removal + // as earlier removals could remove a tree that includes a later removal + if (!mirror.getNode(mutation.id)) { + this.warnNodeNotFound(d, mutation.id); + return false; + } + return true; + }); + d.removes.forEach((mutation) => { const target = mirror.getNode(mutation.id); if (!target) { - if (d.removes.find((r) => r.id === mutation.parentId)) { - // no need to warn, parent was already removed - return; - } - return this.warnNodeNotFound(d, mutation.id); + // no need to warn, parent was already removed + return; } let parent = mirror.getNode(mutation.parentId); if (!parent) { From 16c6393af84872163241b9b9ec9e23460826e36b Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 10 Apr 2023 18:59:20 +1000 Subject: [PATCH 41/52] update rrweb dependencies --- packages/rrweb-cutter/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index 0ecfbc3bbd..4459d5b18b 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -47,9 +47,9 @@ }, "dependencies": { "lodash.clonedeep": "^4.5.0", - "@rrweb/types": "^2.0.0-alpha.5", - "rrdom": "^2.0.0-alpha.5", - "rrweb-snapshot": "^2.0.0-alpha.5", - "rrweb": "^2.0.0-alpha.5" + "@rrweb/types": "^2.0.0-alpha.7", + "rrdom": "^2.0.0-alpha.7", + "rrweb-snapshot": "^2.0.0-alpha.7", + "rrweb": "^2.0.0-alpha.7" } } From 20efefb0f5d38ab3e4a366e2bae99fea1e697cfb Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 24 Apr 2023 17:41:37 +1000 Subject: [PATCH 42/52] add test cases for meta events and input events fix some bugs found while writing test cases --- packages/rrweb-cutter/src/cut-session.ts | 33 +-- .../rrweb-cutter/test/cut-session.test.ts | 95 +++++++- .../test/events/inline-style.event.ts | 1 + .../rrweb-cutter/test/events/input.event.ts | 203 ++++++++++++++++++ packages/rrweb/test/events/input.ts | 1 + 5 files changed, 317 insertions(+), 16 deletions(-) create mode 100644 packages/rrweb-cutter/test/events/input.event.ts diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index 4eb3163f5c..a4a8e7aaa6 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -136,7 +136,7 @@ export function cutSession( } const session = cutEvents( - events.slice(index + 1).filter((e) => e.timestamp <= nextCutTimestamp), + events.slice(index + 1).filter((e) => e.timestamp < nextCutTimestamp), replayer, config, currentTimestamp, @@ -159,7 +159,8 @@ export function getValidSortedPoints(points: number[], totalTime: number) { if (point <= 0 || point >= totalTime) continue; validSortedPoints.push(point); } - return validSortedPoints.sort(); + // The default sortting function will sort the array as string. + return validSortedPoints.sort((a, b) => a - b); } function wrapCutSession( @@ -214,29 +215,33 @@ function cutEvents( metaEvent.timestamp = currentTimestamp; result.push(metaEvent); } - const fullsnapshotDelay = 1, - IncrementalEventDelay = 2; + const FullsnapshotDelay = 1, // The delay between MetaEvent and FullSnapshot. + // The delay between FullSnapshot and MetaEvent. Make sure the iframe event is after the full snapshot. + IFrameEventDelay = 2, + // The delay between IncrementalSnapshot and MetaEvent. Make sure the incremental event is applied after all frames are loaded. + IncrementalEventDelay = 3; const iframeSnapshots: eventWithTime[] = []; + const incrementalEvents: eventWithTime[] = []; const onSerialize = (n: IRRNode) => { - const timestamp = currentTimestamp + fullsnapshotDelay; + const timestamp = currentTimestamp + IncrementalEventDelay; if (n.RRNodeType !== NodeType.Element) return; const rrElement = n as RRElement; rrElement.inputData && - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: rrElement.inputData, timestamp, }); rrElement.scrollData && - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: rrElement.scrollData, timestamp, }); if (rrElement instanceof RRCanvasElement) rrElement.canvasMutations.forEach((canvasData) => - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: canvasData.mutation, timestamp, @@ -244,7 +249,7 @@ function cutEvents( ); else if (rrElement instanceof RRStyleElement) rrElement.rules.forEach((styleRule) => - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: styleRule, timestamp, @@ -252,7 +257,7 @@ function cutEvents( ); else if (rrElement instanceof RRMediaElement) { (rrElement.volume !== undefined || rrElement.muted !== undefined) && - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: { source: IncrementalSource.MediaInteraction, @@ -264,7 +269,7 @@ function cutEvents( timestamp, }); rrElement.playbackRate !== undefined && - result.push({ + incrementalEvents.push({ type: EventType.IncrementalSnapshot, data: { source: IncrementalSource.MediaInteraction, @@ -297,7 +302,7 @@ function cutEvents( attributes: [], isAttachIframe: true, }, - timestamp, + timestamp: currentTimestamp + IFrameEventDelay, }); } }; @@ -326,10 +331,10 @@ function cutEvents( left: replayer.virtualDom.scrollLeft, }, }, - timestamp: currentTimestamp + fullsnapshotDelay, + timestamp: currentTimestamp + FullsnapshotDelay, }; result.push(fullSnapshotEvent); - result = result.concat(iframeSnapshots); + result = result.concat(iframeSnapshots, incrementalEvents); result = result.concat( replayer.unhandledEvents.map((e) => ({ ...e, diff --git a/packages/rrweb-cutter/test/cut-session.test.ts b/packages/rrweb-cutter/test/cut-session.test.ts index 2a0acf08f9..42c0e2006a 100644 --- a/packages/rrweb-cutter/test/cut-session.test.ts +++ b/packages/rrweb-cutter/test/cut-session.test.ts @@ -6,11 +6,12 @@ import fs from 'fs'; import { createMirror, snapshot } from 'rrweb-snapshot'; import { EventType } from 'rrweb'; import { SyncReplayer } from 'rrweb'; -import type { eventWithTime } from '@rrweb/types'; -import { RRDocument, buildFromDom, printRRDom } from 'rrdom'; +import { IncrementalSource, eventWithTime, metaEvent } from '@rrweb/types'; +import { RRDocument, RRElement, buildFromDom, printRRDom } from 'rrdom'; import { cutSession, getValidSortedPoints } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; +import { events as inputEvents } from './events/input.event'; import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; describe('cut session', () => { @@ -193,6 +194,96 @@ describe('cut session', () => { printRRDom(replayer.virtualDom, replayer.getMirror()), ).toMatchSnapshot('screenshot at 1000ms'); }); + + it('new generated session should have a correct meta event', () => { + const cutTime = 1000; + const result = cutSession(mutationEvents, { points: [cutTime] }); + expect(result).toHaveLength(2); + const generatedSession = result[1]; + const originalMetaEvent = mutationEvents.filter( + (event) => event.type === EventType.Meta, + )[0] as metaEvent; + expect(originalMetaEvent.type).toEqual(EventType.Meta); + const metaEvent = generatedSession.events[0] as eventWithTime; + expect(metaEvent.type).toEqual(EventType.Meta); + expect(metaEvent.data).toEqual(originalMetaEvent.data); + expect(metaEvent.timestamp).toEqual(generatedSession.startTimestamp); + }); + + it('should generate correct meta events from multiple meta events', () => { + // TODO + }); + + it('should cut events with input events correctly', () => { + const points = [1000, 1500]; + const results = cutSession(inputEvents, { points }); + expect(results).toHaveLength(3); + + const Input1NodeId = 6, + Input2NodeId = 33, + SelectionNodeId = 26; + + let replayer = new SyncReplayer(results[0].events); + replayer.play(); + let inputElement1 = replayer.getMirror().getNode(Input1NodeId); + expect(inputElement1).not.toBeNull(); + expect((inputElement1 as RRElement).getAttribute('value')).toEqual( + 'valueA', + ); + + // The new session start from 1000ms + replayer = new SyncReplayer(results[1].events); + replayer.play(); + let incrementalInputEvents = results[1].events.filter( + (e) => + e.type === EventType.IncrementalSnapshot && + e.data.source === IncrementalSource.Input, + ); + expect(incrementalInputEvents).toHaveLength(1); + // The incremental input event should be played within 5ms + const inputEvent = incrementalInputEvents[0]; + expect(inputEvent.timestamp - results[1].startTimestamp).toBeLessThan(5); + inputElement1 = replayer.getMirror().getNode(Input1NodeId); + expect(inputElement1).not.toBeNull(); + expect((inputElement1 as RRElement).inputData).toEqual(inputEvent.data); + let selectionElement = replayer.getMirror().getNode(SelectionNodeId); + expect(selectionElement).not.toBeNull(); + expect(selectionElement?.childNodes).toHaveLength(3); + let inputElement2 = replayer.getMirror().getNode(Input2NodeId); + expect(inputElement2).not.toBeNull(); + expect(inputElement2?.nodeType).toBe(document.ELEMENT_NODE); + + // The new session start from 1500ms + replayer = new SyncReplayer(results[2].events); + replayer.play(); + // The incremental input event should be played within 5ms + incrementalInputEvents = results[2].events.filter( + (e) => + e.type === EventType.IncrementalSnapshot && + e.data.source === IncrementalSource.Input, + ); + expect(incrementalInputEvents).toHaveLength(3); + // All incremental input events should be played within 5ms + expect( + incrementalInputEvents.filter( + (e) => e.timestamp - results[2].startTimestamp < 5, + ), + ).toHaveLength(3); + inputElement1 = replayer.getMirror().getNode(Input1NodeId); + expect((inputElement1 as RRElement).inputData).toEqual( + incrementalInputEvents[0].data, + ); + selectionElement = replayer.getMirror().getNode(SelectionNodeId); + expect(selectionElement).not.toBeNull(); + expect((selectionElement as RRElement).inputData).toEqual( + incrementalInputEvents[1].data, + ); + inputElement2 = replayer.getMirror().getNode(Input2NodeId); + expect(inputElement2).not.toBeNull(); + expect((inputElement2 as RRElement).inputData).toEqual( + incrementalInputEvents[2].data, + ); + }); }); describe('Build full snapshot events from RRDom', () => { diff --git a/packages/rrweb-cutter/test/events/inline-style.event.ts b/packages/rrweb-cutter/test/events/inline-style.event.ts index bd024edff5..956b09871d 100644 --- a/packages/rrweb-cutter/test/events/inline-style.event.ts +++ b/packages/rrweb-cutter/test/events/inline-style.event.ts @@ -75,6 +75,7 @@ export const eventsFn = (): eventWithTime[] => [ { type: 2, tagName: 'body', + attributes: {}, childNodes: [ { type: 3, textContent: '\n ', id: 13 }, { diff --git a/packages/rrweb-cutter/test/events/input.event.ts b/packages/rrweb-cutter/test/events/input.event.ts new file mode 100644 index 0000000000..018049ae03 --- /dev/null +++ b/packages/rrweb-cutter/test/events/input.event.ts @@ -0,0 +1,203 @@ +import { EventType, IncrementalSource } from '@rrweb/types'; +import type { eventWithTime } from '@rrweb/types'; + +const now = Date.now(); +export const events: eventWithTime[] = [ + { + type: EventType.DomContentLoaded, + data: {}, + timestamp: now, + }, + { + type: EventType.Load, + data: {}, + timestamp: now + 100, + }, + { + type: EventType.Meta, + data: { + href: 'http://localhost', + width: 1000, + height: 800, + }, + timestamp: now + 100, + }, + // full snapshot: + { + data: { + node: { + id: 1, + type: 0, + childNodes: [ + { id: 2, name: 'html', type: 1, publicId: '', systemId: '' }, + { + id: 3, + type: 2, + tagName: 'html', + attributes: { lang: 'en' }, + childNodes: [ + { + id: 4, + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + }, + { + id: 5, + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { + id: 6, + type: 2, + tagName: 'input', + attributes: { + type: 'text', + value: 'valueA', + id: 'input1', + }, + childNodes: [], + }, + ], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // mutation that adds select elements + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 5, + nextId: null, + node: { + type: 2, + tagName: 'select', + attributes: { + id: 'select1', + }, + childNodes: [], + id: 26, + }, + }, + { + parentId: 26, + nextId: null, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueB' }, + childNodes: [], + id: 27, + }, + }, + { + parentId: 27, + nextId: null, + node: { type: 3, textContent: 'B', id: 28 }, + }, + { + parentId: 26, + nextId: 27, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueC', selected: true }, + childNodes: [], + id: 29, + }, + }, + { + parentId: 26, + nextId: 29, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueD' }, + childNodes: [], + id: 30, + }, + }, + { + parentId: 30, + nextId: null, + node: { type: 3, textContent: 'D', id: 31 }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: 'C', id: 32 }, + }, + { + parentId: 5, + nextId: null, + node: { + type: 2, + tagName: 'input', + attributes: { + id: 'input2', + }, + childNodes: [], + id: 33, + }, + }, + ], + }, + timestamp: now + 1000, + }, + // change the value of the input element '#input1' + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueE', + isChecked: false, + id: 6, + }, + timestamp: now + 1000, + }, + // change the value of the input element '#input2' + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueF', + isChecked: false, + id: 33, + }, + timestamp: now + 1500, + }, + // Change the value of the select element '#select1' + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueG', + isChecked: false, + id: 26, + }, + timestamp: now + 1500, + }, + // A dummy event to increase the session duration + { + type: EventType.Custom, + data: { + tag: 'dummy', + payload: {}, + }, + timestamp: now + 2000, + }, +]; diff --git a/packages/rrweb/test/events/input.ts b/packages/rrweb/test/events/input.ts index e53f8dccb9..a1dedc41a7 100644 --- a/packages/rrweb/test/events/input.ts +++ b/packages/rrweb/test/events/input.ts @@ -74,6 +74,7 @@ const events: eventWithTime[] = [ node: { type: 2, tagName: 'select', + attributes: {}, childNodes: [], id: 26, }, From f6e09c4d832b4bdd49f43bf6407e931b8eb41f1f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 24 Apr 2023 19:20:08 +1000 Subject: [PATCH 43/52] fix failed test cases and fix one bug in the session cutter --- packages/rrweb-cutter/src/cut-session.ts | 6 +++++- .../test/__snapshots__/prune-branches.test.ts.snap | 3 +++ packages/rrweb-cutter/test/events/mutation.event.ts | 2 +- packages/types/tsconfig.json | 3 +-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index a4a8e7aaa6..c1fdce9f8f 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -136,7 +136,11 @@ export function cutSession( } const session = cutEvents( - events.slice(index + 1).filter((e) => e.timestamp < nextCutTimestamp), + events.slice(index + 1).filter((e) => { + // If this would be the last session, include all of the rest events. + if (cutPointIndex >= validSortedTimestamp.length) return true; + return e.timestamp < nextCutTimestamp; + }), replayer, config, currentTimestamp, diff --git a/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap b/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap index f9bd371400..e0b754942f 100644 --- a/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap +++ b/packages/rrweb-cutter/test/__snapshots__/prune-branches.test.ts.snap @@ -59,6 +59,7 @@ exports[`prune branches should keep branches where target child nodes was, and g { \\"type\\": 2, \\"tagName\\": \\"body\\", + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 2, @@ -159,6 +160,7 @@ exports[`prune branches should remove branches based on child nodes that came in { \\"type\\": 2, \\"tagName\\": \\"body\\", + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 2, @@ -250,6 +252,7 @@ exports[`prune branches should remove branches based on nodes that came in after { \\"type\\": 2, \\"tagName\\": \\"body\\", + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 2, diff --git a/packages/rrweb-cutter/test/events/mutation.event.ts b/packages/rrweb-cutter/test/events/mutation.event.ts index 62f9bf10ce..cea3611e2a 100644 --- a/packages/rrweb-cutter/test/events/mutation.event.ts +++ b/packages/rrweb-cutter/test/events/mutation.event.ts @@ -263,7 +263,7 @@ export const events: eventWithTime[] = [ }, ], }, - timestamp: now + 2000, + timestamp: now + 1500, }, // mutation that removes five div elements at 3000ms { diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index ecde939d12..860cfd8e8c 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -12,8 +12,7 @@ "outDir": "build", "lib": ["es6", "dom"], "skipLibCheck": true, - "declaration": true, - "importsNotUsedAsValues": "error" + "declaration": true }, "compileOnSave": true, "exclude": ["test"], From 8aa4bb69135128fe502fbde2fea2300629937512 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 24 Apr 2023 23:46:57 +1000 Subject: [PATCH 44/52] add test cases for cutting events with multiple meta events --- .../rrweb-cutter/test/cut-session.test.ts | 26 ++++++++++++++++++- .../test/events/multi-fullsnapshot.event.ts | 14 ++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts diff --git a/packages/rrweb-cutter/test/cut-session.test.ts b/packages/rrweb-cutter/test/cut-session.test.ts index 42c0e2006a..67be9e100e 100644 --- a/packages/rrweb-cutter/test/cut-session.test.ts +++ b/packages/rrweb-cutter/test/cut-session.test.ts @@ -13,6 +13,7 @@ import { snapshot as RRDomSnapshot } from '../src/snapshot'; import { events as mutationEvents } from './events/mutation.event'; import { events as inputEvents } from './events/input.event'; import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; +import { events as multipleSnapshotEvents } from './events/multi-fullsnapshot.event'; describe('cut session', () => { it('should return the same events if the events length is too short', () => { @@ -211,7 +212,30 @@ describe('cut session', () => { }); it('should generate correct meta events from multiple meta events', () => { - // TODO + const points = [2000, 4000]; + const results = cutSession(multipleSnapshotEvents, { points }); + expect(results).toHaveLength(3); + + // All meta events in the original events + const metaEvents = multipleSnapshotEvents.filter( + (e) => e.type === EventType.Meta, + ); + expect(metaEvents).toHaveLength(2); + + // The first session should have the first meta event + expect( + results[0].events.filter((e) => e.type === EventType.Meta)[0].data, + ).toEqual(metaEvents[0].data); + + // The session after 2000ms should still have the first meta event + expect( + results[1].events.filter((e) => e.type === EventType.Meta)[0].data, + ).toEqual(metaEvents[0].data); + + // The session after 4000ms should start from the second meta event + expect( + results[2].events.filter((e) => e.type === EventType.Meta)[0].data, + ).toEqual(metaEvents[1].data); }); it('should cut events with input events correctly', () => { diff --git a/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts b/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts new file mode 100644 index 0000000000..cbcc802078 --- /dev/null +++ b/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts @@ -0,0 +1,14 @@ +import { eventWithTime } from '@rrweb/types'; +import { events as inputEvents } from './input.event'; +import { events as mutationEvents } from './mutation.event'; + +const duration = + mutationEvents[mutationEvents.length - 1].timestamp - + mutationEvents[0].timestamp; +export const events: eventWithTime[] = [ + ...mutationEvents, + ...inputEvents.map((e) => { + e.timestamp += duration + 1; + return e; + }), +]; From 27f6a2ee7cd5336d46aafe535a60452b7a02b87b Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 2 May 2023 23:29:10 +1000 Subject: [PATCH 45/52] add test case for scroll events and fix a related bug 1. fix the bug: can't generate scroll event from document 2. the previous session doesn't contain the event which is exactly at the cutting point --- packages/rrweb-cutter/src/cut-session.ts | 16 +++- .../rrweb-cutter/test/cut-session.test.ts | 33 ++++++- .../test/events/inline-style.event.ts | 4 +- .../rrweb-cutter/test/events/input.event.ts | 4 +- .../test/events/multi-fullsnapshot.event.ts | 8 +- .../test/events/mutation.event.ts | 8 +- .../rrweb-cutter/test/events/scroll.event.ts | 96 +++++++++++++++++++ 7 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 packages/rrweb-cutter/test/events/scroll.event.ts diff --git a/packages/rrweb-cutter/src/cut-session.ts b/packages/rrweb-cutter/src/cut-session.ts index c1fdce9f8f..7ec456c5f0 100644 --- a/packages/rrweb-cutter/src/cut-session.ts +++ b/packages/rrweb-cutter/src/cut-session.ts @@ -11,6 +11,7 @@ import { RRStyleElement, RRMediaElement, RRIFrameElement, + RRDocument, } from 'rrdom'; import type { IRRNode, RRElement } from 'rrdom'; import { IncrementalSource, EventType, SyncReplayer } from 'rrweb'; @@ -101,7 +102,9 @@ export function cutSession( if (results.length === 0) { results.push( wrapCutSession( - events.slice(0, index + 1), + events + .slice(0, index + 1) + .filter((e) => e.timestamp < validSortedTimestamp[cutPointIndex]), config, baseTimestamp, currentTimestamp, @@ -229,7 +232,16 @@ function cutEvents( const incrementalEvents: eventWithTime[] = []; const onSerialize = (n: IRRNode) => { const timestamp = currentTimestamp + IncrementalEventDelay; - if (n.RRNodeType !== NodeType.Element) return; + if (n.RRNodeType === NodeType.Document) { + const rrDoc = n as RRDocument; + rrDoc.scrollData && + incrementalEvents.push({ + type: EventType.IncrementalSnapshot, + data: rrDoc.scrollData, + timestamp, + }); + return; + } const rrElement = n as RRElement; rrElement.inputData && incrementalEvents.push({ diff --git a/packages/rrweb-cutter/test/cut-session.test.ts b/packages/rrweb-cutter/test/cut-session.test.ts index 67be9e100e..bd9757b99d 100644 --- a/packages/rrweb-cutter/test/cut-session.test.ts +++ b/packages/rrweb-cutter/test/cut-session.test.ts @@ -4,16 +4,16 @@ import path from 'path'; import fs from 'fs'; import { createMirror, snapshot } from 'rrweb-snapshot'; -import { EventType } from 'rrweb'; -import { SyncReplayer } from 'rrweb'; +import { EventType, SyncReplayer } from 'rrweb'; import { IncrementalSource, eventWithTime, metaEvent } from '@rrweb/types'; import { RRDocument, RRElement, buildFromDom, printRRDom } from 'rrdom'; import { cutSession, getValidSortedPoints } from '../src'; import { snapshot as RRDomSnapshot } from '../src/snapshot'; -import { events as mutationEvents } from './events/mutation.event'; -import { events as inputEvents } from './events/input.event'; import { eventsFn as inlineStyleEvents } from './events/inline-style.event'; -import { events as multipleSnapshotEvents } from './events/multi-fullsnapshot.event'; +import mutationEvents from './events/mutation.event'; +import inputEvents from './events/input.event'; +import multipleSnapshotEvents from './events/multi-fullsnapshot.event'; +import scrollEvents from './events/scroll.event'; describe('cut session', () => { it('should return the same events if the events length is too short', () => { @@ -308,6 +308,29 @@ describe('cut session', () => { incrementalInputEvents[2].data, ); }); + + it('should cut the session with scroll events correctly', () => { + const results = cutSession(scrollEvents, { points: [1500] }); + expect(results).toHaveLength(2); + + expect(results[0].events).toHaveLength(5); + expect(results[1].events).toHaveLength(5); + const replayer = new SyncReplayer(results[1].events); + + replayer.play(); + expect((replayer.virtualDom as RRDocument).scrollData).toEqual({ + source: IncrementalSource.Scroll, + id: 1, + x: 200, + y: 250, + }); + expect((replayer.getMirror().getNode(6) as RRElement).scrollData).toEqual({ + source: IncrementalSource.Scroll, + id: 6, + x: 100, + y: 150, + }); + }); }); describe('Build full snapshot events from RRDom', () => { diff --git a/packages/rrweb-cutter/test/events/inline-style.event.ts b/packages/rrweb-cutter/test/events/inline-style.event.ts index 956b09871d..3c4d261004 100644 --- a/packages/rrweb-cutter/test/events/inline-style.event.ts +++ b/packages/rrweb-cutter/test/events/inline-style.event.ts @@ -110,7 +110,7 @@ export const eventsFn = (): eventWithTime[] => [ }, timestamp: now + 200, }, - // mutation that adds a div element at 1000ms + // mutation that adds a div element before 1000ms { type: EventType.IncrementalSnapshot, data: { @@ -120,7 +120,7 @@ export const eventsFn = (): eventWithTime[] => [ removes: [{ parentId: 12, id: 18 }], adds: [], }, - timestamp: now + 1000, + timestamp: now + 999, }, // a placeholder event to extend the duration of the whole session { diff --git a/packages/rrweb-cutter/test/events/input.event.ts b/packages/rrweb-cutter/test/events/input.event.ts index 018049ae03..73dd1eaf21 100644 --- a/packages/rrweb-cutter/test/events/input.event.ts +++ b/packages/rrweb-cutter/test/events/input.event.ts @@ -2,7 +2,7 @@ import { EventType, IncrementalSource } from '@rrweb/types'; import type { eventWithTime } from '@rrweb/types'; const now = Date.now(); -export const events: eventWithTime[] = [ +const events: eventWithTime[] = [ { type: EventType.DomContentLoaded, data: {}, @@ -201,3 +201,5 @@ export const events: eventWithTime[] = [ timestamp: now + 2000, }, ]; + +export default events; diff --git a/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts b/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts index cbcc802078..9d719f532d 100644 --- a/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts +++ b/packages/rrweb-cutter/test/events/multi-fullsnapshot.event.ts @@ -1,14 +1,16 @@ import { eventWithTime } from '@rrweb/types'; -import { events as inputEvents } from './input.event'; -import { events as mutationEvents } from './mutation.event'; +import inputEvents from './input.event'; +import mutationEvents from './mutation.event'; const duration = mutationEvents[mutationEvents.length - 1].timestamp - mutationEvents[0].timestamp; -export const events: eventWithTime[] = [ +const events: eventWithTime[] = [ ...mutationEvents, ...inputEvents.map((e) => { e.timestamp += duration + 1; return e; }), ]; + +export default events; diff --git a/packages/rrweb-cutter/test/events/mutation.event.ts b/packages/rrweb-cutter/test/events/mutation.event.ts index cea3611e2a..b49f48fd2a 100644 --- a/packages/rrweb-cutter/test/events/mutation.event.ts +++ b/packages/rrweb-cutter/test/events/mutation.event.ts @@ -2,7 +2,7 @@ import { EventType, eventWithTime, IncrementalSource } from '@rrweb/types'; const now = Date.now(); -export const events: eventWithTime[] = [ +const events: eventWithTime[] = [ { type: EventType.DomContentLoaded, data: {}, @@ -73,7 +73,7 @@ export const events: eventWithTime[] = [ }, timestamp: now + 100, }, - // mutation that adds five div elements at 1000ms + // mutation that adds five div elements before 1000ms { type: EventType.IncrementalSnapshot, data: { @@ -164,7 +164,7 @@ export const events: eventWithTime[] = [ }, ], }, - timestamp: now + 1000, + timestamp: now + 999, }, // mutation that reverses the order of five div elements at 2000ms { @@ -284,3 +284,5 @@ export const events: eventWithTime[] = [ timestamp: now + 3000, }, ]; + +export default events; diff --git a/packages/rrweb-cutter/test/events/scroll.event.ts b/packages/rrweb-cutter/test/events/scroll.event.ts new file mode 100644 index 0000000000..a44775c8cc --- /dev/null +++ b/packages/rrweb-cutter/test/events/scroll.event.ts @@ -0,0 +1,96 @@ +import { EventType, IncrementalSource } from '@rrweb/types'; +import type { eventWithTime } from '@rrweb/types'; + +const now = Date.now(); +const events: eventWithTime[] = [ + { + type: EventType.DomContentLoaded, + data: {}, + timestamp: now, + }, + { + type: EventType.Load, + data: {}, + timestamp: now + 100, + }, + { + type: EventType.Meta, + data: { + href: 'http://localhost', + width: 1200, + height: 500, + }, + timestamp: now + 100, + }, + // full snapshot: + { + data: { + node: { + id: 1, + type: 0, + childNodes: [ + { id: 2, name: 'html', type: 1, publicId: '', systemId: '' }, + { + id: 3, + type: 2, + tagName: 'html', + attributes: { lang: 'en' }, + childNodes: [ + { + id: 4, + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + }, + { + id: 5, + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'div', + attributes: { + id: 'container', + style: 'height: 1000px; overflow: scroll;', + }, + childNodes: [], + id: 6, + }, + ], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // scroll event on the "#container" div + { + type: EventType.IncrementalSnapshot, + data: { source: IncrementalSource.Scroll, id: 6, x: 100, y: 150 }, + timestamp: now + 1000, + }, + // scroll event on document + { + type: EventType.IncrementalSnapshot, + data: { source: IncrementalSource.Scroll, id: 1, x: 200, y: 250 }, + timestamp: now + 1500, + }, + // A dummy event to increase the session duration + { + type: EventType.Custom, + data: { + tag: 'dummy', + payload: {}, + }, + timestamp: now + 2000, + }, +]; + +export default events; From 4d682e85206b79c1a456841d644988b6ce34c5c1 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 3 May 2023 00:14:04 +1000 Subject: [PATCH 46/52] update browser list db --- yarn.lock | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4f5881d2ff..c9957782c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4362,25 +4362,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219: - version "1.0.30001246" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001246.tgz" - integrity sha512-Tc+ff0Co/nFNbLOrziBXmMVtpt9S2c2Y+Z9Nk9Khj09J+0zR9ejvIW5qkZAErCbOrVODCx/MN+GpB5FNBs5GFA== - -caniuse-lite@^1.0.30001264: - version "1.0.30001265" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz" - integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw== - -caniuse-lite@^1.0.30001286: - version "1.0.30001297" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001297.tgz" - integrity sha512-6bbIbowYG8vFs/Lk4hU9jFt7NknGDleVAciK916tp6ft1j+D//ZwwL6LbF1wXMQ32DMSjeuUV8suhh6dlmFjcA== - -caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001450: - version "1.0.30001451" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" - integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001450: + version "1.0.30001482" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz" + integrity sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ== caseless@~0.12.0: version "0.12.0" From 73af8d6e5cea795e74bf2514521f1bbc1fa15824 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 15 Jun 2023 03:52:51 +1000 Subject: [PATCH 47/52] init demo --- .../examples/browser/example1.html | 0 .../rrweb-cutter/examples/server/.gitignore | 2 + .../rrweb-cutter/examples/server/README.md | 0 .../rrweb-cutter/examples/server/package.json | 22 ++++ .../examples/server/src/client.html | 25 +++++ .../rrweb-cutter/examples/server/src/index.ts | 28 +++++ .../examples/server/tsconfig.json | 25 +++++ .../test/events/adopted-style.event.ts | 102 ++++++++++++++++++ packages/web-extension/src/pages/Player.tsx | 1 + 9 files changed, 205 insertions(+) create mode 100644 packages/rrweb-cutter/examples/browser/example1.html create mode 100644 packages/rrweb-cutter/examples/server/.gitignore create mode 100644 packages/rrweb-cutter/examples/server/README.md create mode 100644 packages/rrweb-cutter/examples/server/package.json create mode 100644 packages/rrweb-cutter/examples/server/src/client.html create mode 100644 packages/rrweb-cutter/examples/server/src/index.ts create mode 100644 packages/rrweb-cutter/examples/server/tsconfig.json create mode 100644 packages/rrweb-cutter/test/events/adopted-style.event.ts 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/test/events/adopted-style.event.ts b/packages/rrweb-cutter/test/events/adopted-style.event.ts new file mode 100644 index 0000000000..d46c4eb29e --- /dev/null +++ b/packages/rrweb-cutter/test/events/adopted-style.event.ts @@ -0,0 +1,102 @@ +import { EventType, IncrementalSource } from '@rrweb/types'; +import type { eventWithTime } from '@rrweb/types'; + +const now = Date.now(); +const events: eventWithTime[] = [ + { + type: EventType.Meta, + data: { + href: 'http://localhost', + width: 1200, + height: 500, + }, + timestamp: now, + }, + // full snapshot: + { + data: { + node: { + id: 1, + type: 0, + childNodes: [ + { id: 2, name: 'html', type: 1, publicId: '', systemId: '' }, + { + id: 3, + type: 2, + tagName: 'html', + attributes: { lang: 'en' }, + childNodes: [ + { + id: 4, + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + }, + { + id: 5, + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 6, + }, + ], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // mutation that adds five div elements before 1000ms + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 6, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + isShadow: true, + id: 7, + }, + }, + ], + }, + timestamp: now + 150, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.AdoptedStyleSheet, + id: 1, + styleIds: [1], + styles: [ + { + styleId: 1, + rules: [], + }, + ], + }, + timestamp: now + 150, + }, +]; + +export default events; diff --git a/packages/web-extension/src/pages/Player.tsx b/packages/web-extension/src/pages/Player.tsx index b9744a8fa4..d920145769 100644 --- a/packages/web-extension/src/pages/Player.tsx +++ b/packages/web-extension/src/pages/Player.tsx @@ -34,6 +34,7 @@ export default function Player() { 'https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css'; linkEl.rel = 'stylesheet'; document.head.appendChild(linkEl); + console.log(`var events = ${JSON.stringify(events, null, 2)}`); playerRef.current = new Replayer({ target: playerElRef.current as HTMLElement, props: { From 2c92fa3ac59e7e0a66ab57f238710ca8d8251a91 Mon Sep 17 00:00:00 2001 From: YunFeng0817 Date: Thu, 12 Dec 2024 08:20:30 +0000 Subject: [PATCH 48/52] Apply formatting changes --- .changeset/gorgeous-eyes-agree.md | 2 +- .changeset/popular-seals-buy.md | 2 +- packages/rrweb-player/.svelte-kit/generated/client/app.js | 1 + .../rrweb-player/.svelte-kit/generated/client/nodes/0.js | 2 +- .../rrweb-player/.svelte-kit/generated/client/nodes/1.js | 2 +- packages/rrweb-player/.svelte-kit/tsconfig.json | 5 ++++- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.changeset/gorgeous-eyes-agree.md b/.changeset/gorgeous-eyes-agree.md index 038ac9c540..2fd6972ebe 100644 --- a/.changeset/gorgeous-eyes-agree.md +++ b/.changeset/gorgeous-eyes-agree.md @@ -1,5 +1,5 @@ --- -'@rrweb/cutter': patch +"@rrweb/cutter": patch --- Feat: Add two tools to process the recording data diff --git a/.changeset/popular-seals-buy.md b/.changeset/popular-seals-buy.md index 7a88ec5c55..31833a8219 100644 --- a/.changeset/popular-seals-buy.md +++ b/.changeset/popular-seals-buy.md @@ -1,5 +1,5 @@ --- -'rrweb': patch +"rrweb": patch --- Feat: Add a Sync Replayer which can be executed in an RRDom environment. diff --git a/packages/rrweb-player/.svelte-kit/generated/client/app.js b/packages/rrweb-player/.svelte-kit/generated/client/app.js index 3ef9588de4..0082123691 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/app.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/app.js @@ -13,6 +13,7 @@ export const dictionary = { export const hooks = { handleError: (({ error }) => { console.error(error) }), + reroute: (() => {}) }; diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js index be42925a92..dd6c86a8ab 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/layout.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/layout.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js index d5b3e24a1e..d6171d8e6d 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/error.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/error.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/tsconfig.json b/packages/rrweb-player/.svelte-kit/tsconfig.json index 548368769b..5580f6b98f 100644 --- a/packages/rrweb-player/.svelte-kit/tsconfig.json +++ b/packages/rrweb-player/.svelte-kit/tsconfig.json @@ -33,7 +33,10 @@ "exclude": [ "../node_modules/**", "../src/service-worker.js", + "../src/service-worker/**/*.js", "../src/service-worker.ts", - "../src/service-worker.d.ts" + "../src/service-worker/**/*.ts", + "../src/service-worker.d.ts", + "../src/service-worker/**/*.d.ts" ] } \ No newline at end of file From 627cedba4349b0850e4f27ccf5e010ff840a5d28 Mon Sep 17 00:00:00 2001 From: yunfeng0817 Date: Thu, 12 Dec 2024 00:34:44 -0800 Subject: [PATCH 49/52] upgrade cutter project setup --- packages/rrdom/jest.config.js | 14 - packages/rrweb-cutter/jest.config.js | 8 - packages/rrweb-cutter/package.json | 32 +- packages/rrweb-cutter/tsconfig.json | 28 +- packages/rrweb-cutter/vite.config.ts | 23 +- packages/rrweb-cutter/vitest.config.ts | 12 + packages/web-extension/src/pages/Player.tsx | 1 - yarn.lock | 356 +------------------- 8 files changed, 49 insertions(+), 425 deletions(-) delete mode 100644 packages/rrdom/jest.config.js delete mode 100644 packages/rrweb-cutter/jest.config.js create mode 100644 packages/rrweb-cutter/vitest.config.ts 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/jest.config.js b/packages/rrweb-cutter/jest.config.js deleted file mode 100644 index 81f6951715..0000000000 --- a/packages/rrweb-cutter/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -export default { - preset: 'ts-jest', - testEnvironment: 'node', - moduleNameMapper: { - 'rrweb/test/utils': '/../rrweb/test/utils', - }, -}; diff --git a/packages/rrweb-cutter/package.json b/packages/rrweb-cutter/package.json index 39d6bd0eab..25984eb728 100644 --- a/packages/rrweb-cutter/package.json +++ b/packages/rrweb-cutter/package.json @@ -1,15 +1,15 @@ { "name": "@rrweb/cutter", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.18", "publishConfig": { "access": "public" }, "scripts": { - "dev": "vite", - "test": "jest", - "build": "tsc -noEmit && vite build", + "dev": "vite build --watch", + "build": "yarn turbo run prepublish", "check-types": "tsc -noEmit", - "prepublish": "npm run build", + "test": "vitest run", + "prepublish": "tsc -noEmit && vite build", "lint": "yarn eslint src/**/*.ts", "prune-events": "npx vite-node scripts/prune.ts --" }, @@ -34,22 +34,20 @@ "module": "dist/index.js", "typings": "dist/index.d.ts", "files": [ - "dist" + "dist", + "package.json" ], "devDependencies": { - "@types/jest": "^27.4.1", - "@types/lodash.clonedeep": "^4.5.7", - "jest": "^27.5.1", - "ts-jest": "^27.1.3", - "typescript": "^4.7.3", - "vite": "^4.1.1", - "vite-plugin-dts": "^1.7.2" + "@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.8", + "@rrweb/types": "^2.0.0-alpha.18", "lodash.clonedeep": "^4.5.0", - "rrdom": "^2.0.0-alpha.8", - "rrweb-snapshot": "^2.0.0-alpha.8", - "rrweb": "^2.0.0-alpha.8" + "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/tsconfig.json b/packages/rrweb-cutter/tsconfig.json index 98cc10e251..79c1d06392 100644 --- a/packages/rrweb-cutter/tsconfig.json +++ b/packages/rrweb-cutter/tsconfig.json @@ -1,39 +1,23 @@ { + "extends": "../../tsconfig.base.json", + "include": ["src"], "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 + "tsBuildInfoFile": "./tsconfig.tsbuildinfo" }, - "compileOnSave": true, - "include": ["src"], - "exclude": ["test"], "references": [ { "path": "../types" }, { - "path": "../rrdom" + "path": "../rrweb-snapshot" }, { - "path": "../rrweb-snapshot" + "path": "../rrdom" }, { "path": "../rrweb" } ] } + diff --git a/packages/rrweb-cutter/vite.config.ts b/packages/rrweb-cutter/vite.config.ts index 7b4d485ba9..7003421dfd 100644 --- a/packages/rrweb-cutter/vite.config.ts +++ b/packages/rrweb-cutter/vite.config.ts @@ -1,21 +1,4 @@ -import dts from 'vite-plugin-dts'; -/** - * @type {import('vite').UserConfig} - */ -export default { - build: { - lib: { - entry: 'src/index.ts', - name: 'rrwebCutter', - fileName: 'index', - formats: ['es', 'cjs', 'umd', 'iife'], - }, +import path from 'path'; +import config from '../../vite.config.default'; - minify: true, - - sourcemap: true, - - emptyOutDir: true, - }, - plugins: [dts()], -}; +export default config(path.resolve(__dirname, 'src/index.ts'), 'rrweb-cutter'); diff --git a/packages/rrweb-cutter/vitest.config.ts b/packages/rrweb-cutter/vitest.config.ts new file mode 100644 index 0000000000..b3d4ebd456 --- /dev/null +++ b/packages/rrweb-cutter/vitest.config.ts @@ -0,0 +1,12 @@ +/// +import { defineProject, mergeConfig } from 'vitest/config'; +import configShared from '../../vitest.config'; + +export default mergeConfig( + configShared, + defineProject({ + test: { + globals: true, + }, + }), +); diff --git a/packages/web-extension/src/pages/Player.tsx b/packages/web-extension/src/pages/Player.tsx index 8a60d78d21..56f3fa4d00 100644 --- a/packages/web-extension/src/pages/Player.tsx +++ b/packages/web-extension/src/pages/Player.tsx @@ -36,7 +36,6 @@ export default function Player() { linkEl.href = `https://cdn.jsdelivr.net/npm/rrweb-player@${rrwebPlayerVersion}/dist/style.min.css`; linkEl.rel = 'stylesheet'; document.head.appendChild(linkEl); - console.log(`var events = ${JSON.stringify(events, null, 2)}`); playerRef.current = new Replayer({ target: playerElRef.current as HTMLElement, props: { diff --git a/yarn.lock b/yarn.lock index 8a93da185a..a6d2f3963c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1858,221 +1858,111 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== -"@esbuild/android-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" - integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== - "@esbuild/android-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== -"@esbuild/android-arm@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" - integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== - "@esbuild/android-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== -"@esbuild/android-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" - integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== - "@esbuild/android-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== -"@esbuild/darwin-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" - integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== - "@esbuild/darwin-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== -"@esbuild/darwin-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" - integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== - "@esbuild/darwin-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== -"@esbuild/freebsd-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" - integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== - "@esbuild/freebsd-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== -"@esbuild/freebsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" - integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== - "@esbuild/freebsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== -"@esbuild/linux-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" - integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== - "@esbuild/linux-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== -"@esbuild/linux-arm@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" - integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== - "@esbuild/linux-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== -"@esbuild/linux-ia32@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" - integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== - "@esbuild/linux-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== -"@esbuild/linux-loong64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" - integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== - "@esbuild/linux-loong64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== -"@esbuild/linux-mips64el@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" - integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== - "@esbuild/linux-mips64el@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== -"@esbuild/linux-ppc64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" - integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== - "@esbuild/linux-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== -"@esbuild/linux-riscv64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" - integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== - "@esbuild/linux-riscv64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== -"@esbuild/linux-s390x@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" - integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== - "@esbuild/linux-s390x@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== -"@esbuild/linux-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" - integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== - "@esbuild/linux-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== -"@esbuild/netbsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" - integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== - "@esbuild/netbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== -"@esbuild/openbsd-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" - integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== - "@esbuild/openbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== -"@esbuild/sunos-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" - integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== - "@esbuild/sunos-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== -"@esbuild/win32-arm64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" - integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== - "@esbuild/win32-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== -"@esbuild/win32-ia32@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" - integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== - "@esbuild/win32-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== -"@esbuild/win32-x64@0.16.17": - version "0.16.17" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" - integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== - "@esbuild/win32-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" @@ -2438,15 +2328,6 @@ resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.6.23.tgz#6ff21c5b1075039cf9f69e2a14807a64edb960c5" integrity sha512-6h/L/id7JiuCcLKNZSliMfl9S159/ditQ/wc4TPlHJ/gcqoo4PNGggVaY6VcvVef9VFGuhh+UW27iAnEzQn+Kw== -"@microsoft/api-extractor-model@7.26.4": - version "7.26.4" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.26.4.tgz#77f2c17140249b846a61eea41e565289cc77181f" - integrity sha512-PDCgCzXDo+SLY5bsfl4bS7hxaeEtnXj7XtuzEE+BtALp7B5mK/NrS2kHWU69pohgsRmEALycQdaQPXoyT2i5MQ== - dependencies: - "@microsoft/tsdoc" "0.14.2" - "@microsoft/tsdoc-config" "~0.16.1" - "@rushstack/node-core-library" "3.55.2" - "@microsoft/api-extractor-model@7.28.13": version "7.28.13" resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz#96fbc52155e0d07e0eabbd9699065b77702fe33a" @@ -2475,24 +2356,6 @@ source-map "~0.6.1" typescript "5.4.2" -"@microsoft/api-extractor@^7.33.5": - version "7.34.4" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.34.4.tgz#80677b5059b437bc07e9e55832c0cbde671c16a1" - integrity sha512-HOdcci2nT40ejhwPC3Xja9G+WSJmWhCUKKryRfQYsmE9cD+pxmBaKBKCbuS9jUcl6bLLb4Gz+h7xEN5r0QiXnQ== - dependencies: - "@microsoft/api-extractor-model" "7.26.4" - "@microsoft/tsdoc" "0.14.2" - "@microsoft/tsdoc-config" "~0.16.1" - "@rushstack/node-core-library" "3.55.2" - "@rushstack/rig-package" "0.3.18" - "@rushstack/ts-command-line" "4.13.2" - colors "~1.2.1" - lodash "~4.17.15" - resolve "~1.22.1" - semver "~7.3.0" - source-map "~0.6.1" - typescript "~4.8.4" - "@microsoft/tsdoc-config@0.16.2", "@microsoft/tsdoc-config@~0.16.1": version "0.16.2" resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" @@ -2669,15 +2532,6 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.3.2.tgz#58cd2bd25df2acc16c628e1b6f6150ea6c7455bc" integrity sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA== -"@rollup/pluginutils@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" - integrity sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA== - dependencies: - "@types/estree" "^1.0.0" - estree-walker "^2.0.2" - picomatch "^2.3.1" - "@rollup/pluginutils@^5.1.0": version "5.1.3" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz#3001bf1a03f3ad24457591f2c259c8e514e0dbdf" @@ -2782,19 +2636,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz#4dff5c4259ebe6c5b4a8f2c5bc3829b7a8447ff0" integrity sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA== -"@rushstack/node-core-library@3.55.2", "@rushstack/node-core-library@^3.53.2": - version "3.55.2" - resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.55.2.tgz#d951470bac98171de13a8a351d4537c63fbfd0b6" - integrity sha512-SaLe/x/Q/uBVdNFK5V1xXvsVps0y7h1sN7aSJllQyFbugyOaxhNRF25bwEDnicARNEjJw0pk0lYnJQ9Kr6ev0A== - dependencies: - colors "~1.2.1" - fs-extra "~7.0.1" - import-lazy "~4.0.0" - jju "~1.4.0" - resolve "~1.22.1" - semver "~7.3.0" - z-schema "~5.0.2" - "@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" @@ -2807,14 +2648,6 @@ semver "~7.5.4" z-schema "~5.0.2" -"@rushstack/rig-package@0.3.18": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.3.18.tgz#2b59eb8ed482e8cd6ad8d396414bf3200efdd682" - integrity sha512-SGEwNTwNq9bI3pkdd01yCaH+gAsHqs0uxfGvtw9b0LJXH52qooWXnrFTRRLG1aL9pf+M2CARdrA9HLHJys3jiQ== - dependencies: - resolve "~1.22.1" - strip-json-comments "~3.1.1" - "@rushstack/rig-package@0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.5.2.tgz#0e23a115904678717a74049661931c0b37dd5495" @@ -2831,16 +2664,6 @@ "@rushstack/node-core-library" "4.0.2" supports-color "~8.1.1" -"@rushstack/ts-command-line@4.13.2": - version "4.13.2" - resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.13.2.tgz#2dfdcf418d58256671433b1da4a3b67e1814cc7a" - integrity sha512-bCU8qoL9HyWiciltfzg7GqdfODUeda/JpI0602kbN5YH22rzTxyqYvv7aRLENCM7XCQ1VRs7nMkEqgJUOU8Sag== - dependencies: - "@types/argparse" "1.0.38" - argparse "~1.0.9" - colors "~1.2.1" - string-argv "~0.3.1" - "@rushstack/ts-command-line@4.19.1": version "4.19.1" resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz#288ee54dd607e558a8be07705869c16c31b5c3ef" @@ -2960,16 +2783,6 @@ resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== -"@ts-morph/common@~0.18.0": - version "0.18.1" - resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" - integrity sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA== - dependencies: - fast-glob "^3.2.12" - minimatch "^5.1.0" - mkdirp "^1.0.4" - path-browserify "^1.0.1" - "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" @@ -3204,10 +3017,10 @@ dependencies: "@types/node" "*" -"@types/lodash.clonedeep@^4.5.7": - version "4.5.7" - resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197" - integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw== +"@types/lodash.clonedeep@^4.5.9": + version "4.5.9" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz#ea48276c7cc18d080e00bb56cf965bcceb3f0fc1" + integrity sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q== dependencies: "@types/lodash" "*" @@ -4638,11 +4451,6 @@ co@^4.6.0: resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -code-block-writer@^11.0.3: - version "11.0.3" - resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" - integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== - code-red@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35" @@ -4688,11 +4496,6 @@ color2k@^2.0.0: resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.2.tgz#ac2b4aea11c822a6bcb70c768b5a289f4fffcebb" integrity sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w== -colors@~1.2.1: - version "1.2.5" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" - integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -5442,34 +5245,6 @@ esbuild-plugin-umd-wrapper@^2.0.0: resolved "https://registry.yarnpkg.com/esbuild-plugin-umd-wrapper/-/esbuild-plugin-umd-wrapper-2.0.3.tgz#66fd8f34fe88c113c594a2498d5abb73d29f7459" integrity sha512-w81fZGyIahBBOvlJdirDJX73qoAfuD9hAQxuHZFKM+LZjsQo5To+1CFId+RYKk6PKEJZcE8yZ8LK/vyFoLqzHg== -esbuild@^0.16.14: - version "0.16.17" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" - integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== - optionalDependencies: - "@esbuild/android-arm" "0.16.17" - "@esbuild/android-arm64" "0.16.17" - "@esbuild/android-x64" "0.16.17" - "@esbuild/darwin-arm64" "0.16.17" - "@esbuild/darwin-x64" "0.16.17" - "@esbuild/freebsd-arm64" "0.16.17" - "@esbuild/freebsd-x64" "0.16.17" - "@esbuild/linux-arm" "0.16.17" - "@esbuild/linux-arm64" "0.16.17" - "@esbuild/linux-ia32" "0.16.17" - "@esbuild/linux-loong64" "0.16.17" - "@esbuild/linux-mips64el" "0.16.17" - "@esbuild/linux-ppc64" "0.16.17" - "@esbuild/linux-riscv64" "0.16.17" - "@esbuild/linux-s390x" "0.16.17" - "@esbuild/linux-x64" "0.16.17" - "@esbuild/netbsd-x64" "0.16.17" - "@esbuild/openbsd-x64" "0.16.17" - "@esbuild/sunos-x64" "0.16.17" - "@esbuild/win32-arm64" "0.16.17" - "@esbuild/win32-ia32" "0.16.17" - "@esbuild/win32-x64" "0.16.17" - esbuild@^0.21.3: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -5954,17 +5729,6 @@ fast-glob@^3.1.1: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.12: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -7750,11 +7514,6 @@ known-css-properties@^0.35.0: resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.35.0.tgz#f6f8e40ab4e5700fa32f5b2ef5218a56bc853bd6" integrity sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A== -kolorist@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.7.0.tgz#8e22bc470ea2d2743dbd461808f8b5246b19f5f4" - integrity sha512-ymToLHqL02udwVdbkowNpzjFd6UzozMtshPQKVi5k1EjKRqKqBrOnE9QbLEb0/pV76SAiIT13hdL8R6suc+f3g== - kolorist@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" @@ -8187,13 +7946,6 @@ min-indent@^1.0.0: dependencies: brace-expansion "^1.1.7" -minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -8244,11 +7996,6 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mlly@^1.7.2, mlly@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.3.tgz#d86c0fcd8ad8e16395eb764a5f4b831590cee48c" @@ -8342,11 +8089,6 @@ nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -8934,15 +8676,6 @@ postcss-selector-parser@^6.1.0: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss@^8.4.21: - version "8.4.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" - integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - postcss@^8.4.38, postcss@^8.4.39, postcss@^8.4.43: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" @@ -9454,15 +9187,6 @@ resolve@^1.10.0, resolve@^1.19.0, resolve@^1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.22.1, resolve@~1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - resolve@~1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" @@ -9471,6 +9195,15 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" +resolve@~1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -9512,13 +9245,6 @@ rimraf@~2.4.0: dependencies: glob "^6.0.1" -rollup@^3.10.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.15.0.tgz#6f4105e8c4b8145229657b74ad660b02fbfacc05" - integrity sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg== - optionalDependencies: - fsevents "~2.3.2" - rollup@^4.20.0: version "4.28.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.28.1.tgz#7718ba34d62b449dfc49adbfd2f312b4fe0df4de" @@ -9694,13 +9420,6 @@ semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -semver@~7.3.0: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - semver@~7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -9858,11 +9577,6 @@ source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - source-map-support@0.5.21: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -10462,14 +10176,6 @@ ts-jest@^27.1.3: semver "7.x" yargs-parser "20.x" -ts-morph@17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" - integrity sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g== - dependencies: - "@ts-morph/common" "~0.18.0" - code-block-writer "^11.0.3" - ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -10671,21 +10377,11 @@ typescript@5.4.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== -typescript@^4.7.3: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== - typescript@^5.0.3, typescript@^5.4.5: version "5.7.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== -typescript@~4.8.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -10854,20 +10550,6 @@ vite-node@1.6.0: picocolors "^1.0.0" vite "^5.0.0" -vite-plugin-dts@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-1.7.2.tgz#75c581b5c015a5b437d213454e1fd2bc94223b43" - integrity sha512-55Jwfv6n8gAlRSVGCpIY13TCqadtaKex9d2mCbaSxMFAU06suMDsFhIch1x55eptpC2RpeiresqbTjhN2HSEtQ== - dependencies: - "@microsoft/api-extractor" "^7.33.5" - "@rollup/pluginutils" "^5.0.2" - "@rushstack/node-core-library" "^3.53.2" - debug "^4.3.4" - fast-glob "^3.2.12" - fs-extra "^10.1.0" - kolorist "^1.6.0" - ts-morph "17.0.1" - vite-plugin-dts@^3.8.1, vite-plugin-dts@^3.9.1: version "3.9.1" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz#625ad388ec3956708ccec7960550a7b0a8e8909e" @@ -10906,18 +10588,6 @@ vite-plugin-zip-pack@^1.2.2: dependencies: jszip "^3.10.1" -vite@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.1.1.tgz#3b18b81a4e85ce3df5cbdbf4c687d93ebf402e6b" - integrity sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg== - dependencies: - esbuild "^0.16.14" - postcss "^8.4.21" - resolve "^1.22.1" - rollup "^3.10.0" - optionalDependencies: - fsevents "~2.3.2" - vite@^5.0.0, "vite@^5.0.0 || ^4.1.4", vite@^5.2.8, vite@^5.3.1: version "5.4.11" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" From 4327db3798e5c258cf138f333377f92a224b90db Mon Sep 17 00:00:00 2001 From: yunfeng0817 Date: Thu, 12 Dec 2024 00:38:43 -0800 Subject: [PATCH 50/52] restore --- packages/rrweb-player/.svelte-kit/generated/client/app.js | 1 - .../rrweb-player/.svelte-kit/generated/client/nodes/0.js | 2 +- .../rrweb-player/.svelte-kit/generated/client/nodes/1.js | 2 +- packages/rrweb-player/.svelte-kit/tsconfig.json | 5 +---- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/rrweb-player/.svelte-kit/generated/client/app.js b/packages/rrweb-player/.svelte-kit/generated/client/app.js index 0082123691..3ef9588de4 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/app.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/app.js @@ -13,7 +13,6 @@ export const dictionary = { export const hooks = { handleError: (({ error }) => { console.error(error) }), - reroute: (() => {}) }; diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js index dd6c86a8ab..be42925a92 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/layout.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/layout.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js index d6171d8e6d..d5b3e24a1e 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/error.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/error.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/tsconfig.json b/packages/rrweb-player/.svelte-kit/tsconfig.json index 5580f6b98f..548368769b 100644 --- a/packages/rrweb-player/.svelte-kit/tsconfig.json +++ b/packages/rrweb-player/.svelte-kit/tsconfig.json @@ -33,10 +33,7 @@ "exclude": [ "../node_modules/**", "../src/service-worker.js", - "../src/service-worker/**/*.js", "../src/service-worker.ts", - "../src/service-worker/**/*.ts", - "../src/service-worker.d.ts", - "../src/service-worker/**/*.d.ts" + "../src/service-worker.d.ts" ] } \ No newline at end of file From 26db37293668652f0967a9bf98cf7c31893ebf1d Mon Sep 17 00:00:00 2001 From: YunFeng0817 Date: Thu, 12 Dec 2024 08:44:27 +0000 Subject: [PATCH 51/52] Apply formatting changes --- packages/rrweb-player/.svelte-kit/generated/client/app.js | 1 + .../rrweb-player/.svelte-kit/generated/client/nodes/0.js | 2 +- .../rrweb-player/.svelte-kit/generated/client/nodes/1.js | 2 +- packages/rrweb-player/.svelte-kit/tsconfig.json | 5 ++++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/rrweb-player/.svelte-kit/generated/client/app.js b/packages/rrweb-player/.svelte-kit/generated/client/app.js index 3ef9588de4..0082123691 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/app.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/app.js @@ -13,6 +13,7 @@ export const dictionary = { export const hooks = { handleError: (({ error }) => { console.error(error) }), + reroute: (() => {}) }; diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js index be42925a92..dd6c86a8ab 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/0.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/layout.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/layout.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js index d5b3e24a1e..d6171d8e6d 100644 --- a/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js +++ b/packages/rrweb-player/.svelte-kit/generated/client/nodes/1.js @@ -1 +1 @@ -export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/error.svelte"; \ No newline at end of file +export { default as component } from "../../../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-4/error.svelte"; \ No newline at end of file diff --git a/packages/rrweb-player/.svelte-kit/tsconfig.json b/packages/rrweb-player/.svelte-kit/tsconfig.json index 548368769b..5580f6b98f 100644 --- a/packages/rrweb-player/.svelte-kit/tsconfig.json +++ b/packages/rrweb-player/.svelte-kit/tsconfig.json @@ -33,7 +33,10 @@ "exclude": [ "../node_modules/**", "../src/service-worker.js", + "../src/service-worker/**/*.js", "../src/service-worker.ts", - "../src/service-worker.d.ts" + "../src/service-worker/**/*.ts", + "../src/service-worker.d.ts", + "../src/service-worker/**/*.d.ts" ] } \ No newline at end of file From 491a175af95e8b0f6cf92a18c84b16eaa2f7a308 Mon Sep 17 00:00:00 2001 From: yunfeng0817 Date: Thu, 2 Jan 2025 23:08:16 -0800 Subject: [PATCH 52/52] adapt new changes of replayer to sync-replayer except media manager and dialog --- packages/rrweb/src/replay/sync-replayer.ts | 124 ++++++++++++++------- 1 file changed, 85 insertions(+), 39 deletions(-) diff --git a/packages/rrweb/src/replay/sync-replayer.ts b/packages/rrweb/src/replay/sync-replayer.ts index 8a01ecefe7..4983975137 100644 --- a/packages/rrweb/src/replay/sync-replayer.ts +++ b/packages/rrweb/src/replay/sync-replayer.ts @@ -1,40 +1,23 @@ import { rebuild, + adaptCssForReplay, buildNodeWithSN, - NodeType, - BuildCache, + type BuildCache, createCache, Mirror, +} from 'rrweb-snapshot'; +import type { attributes, serializedElementNodeWithId, IMirror, -} from 'rrweb-snapshot'; -import { RRDocument as BaseRRDocument } from 'rrdom'; -import type { - RRNode, - RRElement, - RRStyleElement, - RRIFrameElement, - RRMediaElement, - RRCanvasElement, - Mirror as RRDOMMirror, -} from 'rrdom'; -import * as mittProxy from 'mitt'; -import type { playerConfig } from '../types'; -import { - EventType, - IncrementalSource, fullSnapshotEvent, eventWithTime, - MouseInteractions, playerMetaData, addedNodeMutation, incrementalSnapshotEvent, incrementalData, - ReplayerEvents, Handler, Emitter, - MediaInteractions, metaEvent, mutationData, styleValueWithPriority, @@ -45,6 +28,26 @@ import { styleDeclarationData, adoptedStyleSheetData, } from '@rrweb/types'; +import { + EventType, + IncrementalSource, + MouseInteractions, + NodeType, + ReplayerEvents, + MediaInteractions, +} from '@rrweb/types'; +import { RRDocument as BaseRRDocument } from 'rrdom'; +import type { + RRNode, + RRElement, + RRStyleElement, + RRIFrameElement, + RRMediaElement, + RRCanvasElement, + Mirror as RRDOMMirror, +} from 'rrdom'; +import * as mittProxy from 'mitt'; +import type { playerConfig } from '../types'; import { queueToResolveTrees, iterateResolveTree, @@ -150,7 +153,7 @@ export class SyncReplayer { // maybe we can cache it for performance optimization const firstMeta = this.events.find((e) => e.type === EventType.Meta); if (firstMeta) { - const { width, height } = firstMeta.data as metaEvent['data']; + const { width, height } = firstMeta.data; setTimeout(() => { this.emitter.emit(ReplayerEvents.Resize, { width, @@ -249,6 +252,7 @@ export class SyncReplayer { */ public destroy() { this.emitter.emit(ReplayerEvents.Destroy); + this.mirror.reset(); } public startLive() { @@ -345,10 +349,10 @@ export class SyncReplayer { ); } this.legacy_missingNodeRetryMap = {}; - const collected: AppendedIframe[] = []; + const collectedIframes: AppendedIframe[] = []; const afterAppend = (builtNode: Node, id: number) => { this.collectIframeAndAttachDocument( - collected, + collectedIframes, builtNode as unknown as RRNode, ); for (const plugin of this.config.plugins || []) { @@ -369,7 +373,7 @@ export class SyncReplayer { }); afterAppend(this.virtualDom as unknown as Document, event.data.node.id); - for (const { mutationInQueue, builtNode } of collected) { + for (const { mutationInQueue, builtNode } of collectedIframes) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( (m) => m !== mutationInQueue, @@ -382,10 +386,10 @@ export class SyncReplayer { mutation: addedNodeMutation, iframeEl: RRIFrameElement, ) { - const collected: AppendedIframe[] = []; + const collectedIframes: AppendedIframe[] = []; const afterAppend = (builtNode: Node, id: number) => { this.collectIframeAndAttachDocument( - collected, + collectedIframes, builtNode as unknown as RRNode, ); @@ -411,7 +415,7 @@ export class SyncReplayer { mutation.node.id, ); - for (const { mutationInQueue, builtNode } of collected) { + for (const { mutationInQueue, builtNode } of collectedIframes) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( (m) => m !== mutationInQueue, @@ -493,8 +497,8 @@ export class SyncReplayer { case MouseInteractions.TouchStart: case MouseInteractions.TouchEnd: this.mousePos = { - x: d.x, - y: d.y, + x: d.x || 0, + y: d.y || 0, id: d.id, debugData: d, }; @@ -777,14 +781,39 @@ export class SyncReplayer { if ( parentSn && parentSn.type === NodeType.Element && - parentSn.tagName === 'textarea' && mutation.node.type === NodeType.Text ) { - // https://github.com/rrweb-io/rrweb/issues/745 - // parent is textarea, will only keep one child node as the value - for (const c of parent.childNodes) { - if (c.nodeType === parent.TEXT_NODE) { - parent.removeChild(c); + const prospectiveSiblings: RRNode[] = Array.isArray(parent.childNodes) + ? parent.childNodes + : Array.from(parent.childNodes); + if (parentSn.tagName === 'textarea') { + // This should be redundant now as we are either recording the value or the childNode, and not both + // keeping around for backwards compatibility with old bad double data, see + + // https://github.com/rrweb-io/rrweb/issues/745 + // parent is textarea, will only keep one child node as the value + for (const c of prospectiveSiblings) { + if (c.nodeType === parent.TEXT_NODE) { + parent.removeChild(c); + } + } + } else if ( + parentSn.tagName === 'style' && + prospectiveSiblings.length === 1 + ) { + // https://github.com/rrweb-io/rrweb/pull/1417 + /** + * If both _cssText and textContent are present for a style element due to some existing bugs, the element was ending up with two child text nodes + * We need to remove the textNode created by _cssText as it doesn't have an id in the mirror, and thus cannot be further mutated. + */ + for (const cssText of prospectiveSiblings as (Node & RRNode)[]) { + if ( + cssText.nodeType === parent.TEXT_NODE && + !mirror.hasNode(cssText) + ) { + target.textContent = cssText.textContent; + parent.removeChild(cssText); + } } } } else if (parentSn?.type === NodeType.Document) { @@ -908,7 +937,13 @@ export class SyncReplayer { } return this.warnNodeNotFound(d, mutation.id); } - target.textContent = mutation.value; + const parentEl = target.parentElement as RRElement; + if (mutation.value && parentEl && parentEl.tagName === 'STYLE') { + // assumes hackCss: true (which isn't currently configurable from rrweb) + target.textContent = adaptCssForReplay(mutation.value, this.cache); + } else { + target.textContent = mutation.value; + } /** * https://github.com/rrweb-io/rrweb/pull/865 @@ -965,7 +1000,18 @@ export class SyncReplayer { // for safe } } - (target as RRElement).setAttribute(attributeName, value); + if (attributeName === 'value' && target.nodeName === 'TEXTAREA') { + // this may or may not have an effect on the value property (which is what is displayed) + // depending on whether the textarea has been modified by the user yet + const textarea = target; + textarea.childNodes.forEach((c) => textarea.removeChild(c)); + const tn = target.ownerDocument?.createTextNode(value); + if (tn) { + textarea.appendChild(tn); + } + } else { + (target as RRElement).setAttribute(attributeName, value); + } } catch (error) { this.warn( 'An error occurred may due to the checkout feature.', @@ -982,7 +1028,7 @@ export class SyncReplayer { const svp = styleValues[s] as styleValueWithPriority; targetEl.style.setProperty(s, svp[0], svp[1]); } else { - const svs = styleValues[s] as string; + const svs = styleValues[s]; targetEl.style.setProperty(s, svs); } }