From 539f7c8c06e6ff1c2f4768c6aa4cafead8b30ec5 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Mon, 4 Apr 2022 16:20:49 +0800 Subject: [PATCH 001/201] update typings --- packages/rrweb/typings/record/shadow-dom-manager.d.ts | 3 +++ packages/rrweb/typings/replay/index.d.ts | 1 + packages/rrweb/typings/types.d.ts | 1 + 3 files changed, 5 insertions(+) diff --git a/packages/rrweb/typings/record/shadow-dom-manager.d.ts b/packages/rrweb/typings/record/shadow-dom-manager.d.ts index aab8669e5b..e544e2f6b5 100644 --- a/packages/rrweb/typings/record/shadow-dom-manager.d.ts +++ b/packages/rrweb/typings/record/shadow-dom-manager.d.ts @@ -7,6 +7,7 @@ export declare class ShadowDomManager { private scrollCb; private bypassOptions; private mirror; + private restorePatches; constructor(options: { mutationCb: mutationCallBack; scrollCb: scrollCallback; @@ -14,5 +15,7 @@ export declare class ShadowDomManager { mirror: Mirror; }); addShadowRoot(shadowRoot: ShadowRoot, doc: Document): void; + observeAttachShadow(iframeElement: HTMLIFrameElement): void; + reset(): void; } export {}; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index 05e89b38be..1f8a5a1455 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -59,6 +59,7 @@ export declare class Replayer { private applyMutation; private applyScroll; private applyInput; + private applyText; private legacy_resolveMissingNode; private moveAndHover; private drawMouseTail; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 87c1a9c814..ae3aa94ca3 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -494,4 +494,5 @@ declare global { } } export declare type IWindow = Window & typeof globalThis; +export declare type Optional = Pick, K> & Omit; export {}; From e4f680e8c9177fa64d8ae76be9d82ac4e651824b Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Wed, 6 Apr 2022 17:56:39 +0200 Subject: [PATCH 002/201] Remove INode (`node.__sn`) and use Mirror as source of truth (#868) * Move ids to weakmap * Fix typo * Move from INode to storing serialized data in mirror * Update packages/rrweb-snapshot/src/rebuild.ts Co-authored-by: Yun Feng * Remove unnessisary `as Node` typecastings Fixes: https://github.com/rrweb-io/rrweb/pull/868#discussion_r842240758 * Remove unnessisary `as unknown as ...` * Remove unnessisary `as unknown as ...` * Reset mirror when recording starts Solves: https://github.com/rrweb-io/rrweb/pull/868#discussion_r842249599 * API has changed for snapshot, change test to reflect that * Allow for es5 compatibility * Remove unnessisary as unknown as ... and change test to reflect the API change * Refactor mirror to remove `nodeIdMap` Fixes: https://github.com/rrweb-io/rrweb/pull/868#discussion_r842732696 Co-authored-by: Yun Feng --- packages/rrweb-snapshot/README.md | 2 +- packages/rrweb-snapshot/src/css.ts | 2 +- packages/rrweb-snapshot/src/rebuild.ts | 69 ++++---- packages/rrweb-snapshot/src/snapshot.ts | 110 ++++++------ packages/rrweb-snapshot/src/types.ts | 7 +- packages/rrweb-snapshot/src/utils.ts | 73 +++++++- .../rrweb-snapshot/test/integration.test.ts | 14 +- packages/rrweb-snapshot/test/snapshot.test.ts | 3 +- packages/rrweb-snapshot/typings/rebuild.d.ts | 16 +- packages/rrweb-snapshot/typings/snapshot.d.ts | 18 +- packages/rrweb-snapshot/typings/types.d.ts | 5 +- packages/rrweb-snapshot/typings/utils.d.ts | 19 +- packages/rrweb/src/record/iframe-manager.ts | 12 +- packages/rrweb/src/record/index.ts | 27 +-- packages/rrweb/src/record/mutation.ts | 73 ++++---- packages/rrweb/src/record/observer.ts | 30 ++-- .../rrweb/src/record/observers/canvas/2d.ts | 5 +- .../record/observers/canvas/canvas-manager.ts | 5 +- .../src/record/observers/canvas/canvas.ts | 4 +- .../src/record/observers/canvas/webgl.ts | 7 +- .../rrweb/src/record/shadow-dom-manager.ts | 2 +- packages/rrweb/src/replay/canvas/2d.ts | 2 +- packages/rrweb/src/replay/index.ts | 165 +++++++++--------- packages/rrweb/src/replay/virtual-styles.ts | 6 +- packages/rrweb/src/types.ts | 12 +- packages/rrweb/src/utils.ts | 82 ++------- .../rrweb/typings/record/iframe-manager.d.ts | 4 +- packages/rrweb/typings/record/index.d.ts | 2 +- .../typings/record/observers/canvas/2d.d.ts | 3 +- .../observers/canvas/canvas-manager.d.ts | 3 +- .../record/observers/canvas/webgl.d.ts | 3 +- .../typings/record/shadow-dom-manager.d.ts | 3 +- packages/rrweb/typings/replay/index.d.ts | 3 +- .../rrweb/typings/replay/virtual-styles.d.ts | 3 +- packages/rrweb/typings/types.d.ts | 12 +- packages/rrweb/typings/utils.d.ts | 18 +- 36 files changed, 442 insertions(+), 382 deletions(-) diff --git a/packages/rrweb-snapshot/README.md b/packages/rrweb-snapshot/README.md index 934f903483..596a52b70d 100644 --- a/packages/rrweb-snapshot/README.md +++ b/packages/rrweb-snapshot/README.md @@ -37,4 +37,4 @@ There are several things will be done during rebuild: #### buildNodeWithSN -`buildNodeWithSN` will build DOM from serialized node and store serialized information in `__sn` property. +`buildNodeWithSN` will build DOM from serialized node and store serialized information in the `mirror.getMeta(node)`. diff --git a/packages/rrweb-snapshot/src/css.ts b/packages/rrweb-snapshot/src/css.ts index 950ec0b4c1..c0b4001fd4 100644 --- a/packages/rrweb-snapshot/src/css.ts +++ b/packages/rrweb-snapshot/src/css.ts @@ -892,7 +892,7 @@ function addParent(obj: Stylesheet, parent?: Stylesheet) { addParent(v, childParent); }); } else if (value && typeof value === 'object') { - addParent((value as unknown) as Stylesheet, childParent); + addParent(value as Stylesheet, childParent); } } diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 847cdd0698..cf818fc7e3 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -4,11 +4,9 @@ import { NodeType, tagMap, elementNode, - idNodeMap, - INode, BuildCache, } from './types'; -import { isElement } from './utils'; +import { isElement, Mirror } from './utils'; const tagMap: tagMap = { script: 'noscript', @@ -215,7 +213,10 @@ function buildNode( n.attributes.rr_dataURL ) { // backup original img srcset - node.setAttribute('rrweb-original-srcset', n.attributes.srcset as string); + node.setAttribute( + 'rrweb-original-srcset', + n.attributes.srcset as string, + ); } else { node.setAttribute(name, value); } @@ -307,16 +308,16 @@ export function buildNodeWithSN( n: serializedNodeWithId, options: { doc: Document; - map: idNodeMap; + mirror: Mirror; skipChild?: boolean; hackCss: boolean; - afterAppend?: (n: INode) => unknown; + afterAppend?: (n: Node) => unknown; cache: BuildCache; }, -): INode | null { +): Node | null { const { doc, - map, + mirror, skipChild = false, hackCss = true, afterAppend, @@ -328,7 +329,7 @@ export function buildNodeWithSN( } if (n.rootId) { console.assert( - ((map[n.rootId] as unknown) as Document) === doc, + (mirror.getNode(n.rootId) as Document) === doc, 'Target document should has the same root id.', ); } @@ -362,8 +363,7 @@ export function buildNodeWithSN( node = doc; } - (node as INode).__sn = n; - map[n.id] = node as INode; + mirror.add(node, n); if ( (n.type === NodeType.Document || n.type === NodeType.Element) && @@ -372,7 +372,7 @@ export function buildNodeWithSN( for (const childN of n.childNodes) { const childNode = buildNodeWithSN(childN, { doc, - map, + mirror, skipChild: false, hackCss, afterAppend, @@ -394,27 +394,27 @@ export function buildNodeWithSN( } } - return node as INode; + return node; } -function visit(idNodeMap: idNodeMap, onVisit: (node: INode) => void) { - function walk(node: INode) { +function visit(mirror: Mirror, onVisit: (node: Node) => void) { + function walk(node: Node) { onVisit(node); } - for (const key in idNodeMap) { - if (idNodeMap[key]) { - walk(idNodeMap[key]); + for (const id of mirror.getIds()) { + if (mirror.has(id)) { + walk(mirror.getNode(id)!); } } } -function handleScroll(node: INode) { - const n = node.__sn; - if (n.type !== NodeType.Element) { +function handleScroll(node: Node, mirror: Mirror) { + const n = mirror.getMeta(node); + if (n?.type !== NodeType.Element) { return; } - const el = (node as Node) as HTMLElement; + const el = node as HTMLElement; for (const name in n.attributes) { if (!(n.attributes.hasOwnProperty(name) && name.startsWith('rr_'))) { continue; @@ -433,29 +433,36 @@ function rebuild( n: serializedNodeWithId, options: { doc: Document; - onVisit?: (node: INode) => unknown; + onVisit?: (node: Node) => unknown; hackCss?: boolean; - afterAppend?: (n: INode) => unknown; + afterAppend?: (n: Node) => unknown; cache: BuildCache; + mirror: Mirror; }, -): [Node | null, idNodeMap] { - const { doc, onVisit, hackCss = true, afterAppend, cache } = options; - const idNodeMap: idNodeMap = {}; +): Node | null { + const { + doc, + onVisit, + hackCss = true, + afterAppend, + cache, + mirror = new Mirror(), + } = options; const node = buildNodeWithSN(n, { doc, - map: idNodeMap, + mirror, skipChild: false, hackCss, afterAppend, cache, }); - visit(idNodeMap, (visitedNode) => { + visit(mirror, (visitedNode) => { if (onVisit) { onVisit(visitedNode); } - handleScroll(visitedNode); + handleScroll(visitedNode, mirror); }); - return [node, idNodeMap]; + return node; } export default rebuild; diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 6c283b796a..9ccb67b52d 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -3,8 +3,6 @@ import { serializedNodeWithId, NodeType, attributes, - INode, - idNodeMap, MaskInputOptions, SlimDOMOptions, DataURLOptions, @@ -14,6 +12,7 @@ import { ICanvas, } from './types'; import { + Mirror, is2DCanvasBlank, isElement, isShadowRoot, @@ -377,6 +376,7 @@ function serializeNode( n: Node, options: { doc: Document; + mirror: Mirror; blockClass: string | RegExp; blockSelector: string | null; maskTextClass: string | RegExp; @@ -393,6 +393,7 @@ function serializeNode( ): serializedNode | false { const { doc, + mirror, blockClass, blockSelector, maskTextClass, @@ -408,8 +409,8 @@ function serializeNode( } = options; // Only record root id when document object is not the base document let rootId: number | undefined; - if (((doc as unknown) as INode).__sn) { - const docId = ((doc as unknown) as INode).__sn.id; + if (mirror.getMeta(doc)) { + const docId = mirror.getId(doc); rootId = docId === 1 ? undefined : docId; } switch (n.nodeType) { @@ -786,10 +787,10 @@ function slimDOMExcluded( } export function serializeNodeWithId( - n: Node | INode, + n: Node, options: { doc: Document; - map: idNodeMap; + mirror: Mirror; blockClass: string | RegExp; blockSelector: string | null; maskTextClass: string | RegExp; @@ -805,14 +806,17 @@ export function serializeNodeWithId( inlineImages?: boolean; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; + onSerialize?: (n: Node) => unknown; + onIframeLoad?: ( + iframeNode: HTMLIFrameElement, + node: serializedNodeWithId, + ) => unknown; iframeLoadTimeout?: number; }, ): serializedNodeWithId | null { const { doc, - map, + mirror, blockClass, blockSelector, maskTextClass, @@ -834,6 +838,7 @@ export function serializeNodeWithId( let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { doc, + mirror, blockClass, blockSelector, maskTextClass, @@ -853,10 +858,10 @@ export function serializeNodeWithId( return null; } - let id; - // Try to reuse the previous id - if ('__sn' in n) { - id = n.__sn.id; + let id: number | undefined; + if (mirror.hasNode(n)) { + // Reuse the previous id + id = mirror.getId(n); } else if ( slimDOMExcluded(_serializedNode, slimDOMOptions) || (!preserveWhiteSpace && @@ -868,14 +873,16 @@ export function serializeNodeWithId( } else { id = genId(); } - const serializedNode = Object.assign(_serializedNode, { id }); - (n as INode).__sn = serializedNode; if (id === IGNORED_NODE) { return null; // slimDOM } - map[id] = n as INode; + + const serializedNode = Object.assign(_serializedNode, { id }); + + mirror.add(n, serializedNode); + if (onSerialize) { - onSerialize(n as INode); + onSerialize(n); } let recordChild = !skipChild; if (serializedNode.type === NodeType.Element) { @@ -891,15 +898,15 @@ export function serializeNodeWithId( ) { if ( slimDOMOptions.headWhitespace && - _serializedNode.type === NodeType.Element && - _serializedNode.tagName === 'head' + serializedNode.type === NodeType.Element && + serializedNode.tagName === 'head' // would impede performance: || getComputedStyle(n)['white-space'] === 'normal' ) { preserveWhiteSpace = false; } const bypassOptions = { doc, - map, + mirror, blockClass, blockSelector, maskTextClass, @@ -952,7 +959,7 @@ export function serializeNodeWithId( if (iframeDoc && onIframeLoad) { const serializedIframeNode = serializeNodeWithId(iframeDoc, { doc: iframeDoc, - map, + mirror, blockClass, blockSelector, maskTextClass, @@ -974,7 +981,7 @@ export function serializeNodeWithId( }); if (serializedIframeNode) { - onIframeLoad(n as INode, serializedIframeNode); + onIframeLoad(n as HTMLIFrameElement, serializedIframeNode); } } }, @@ -988,6 +995,7 @@ export function serializeNodeWithId( function snapshot( n: Document, options?: { + mirror?: Mirror; blockClass?: string | RegExp; blockSelector?: string | null; maskTextClass?: string | RegExp; @@ -1001,13 +1009,17 @@ function snapshot( inlineImages?: boolean; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; + onSerialize?: (n: Node) => unknown; + onIframeLoad?: ( + iframeNode: HTMLIFrameElement, + node: serializedNodeWithId, + ) => unknown; iframeLoadTimeout?: number; keepIframeSrcFn?: KeepIframeSrcFn; }, -): [serializedNodeWithId | null, idNodeMap] { +): serializedNodeWithId | null { const { + mirror = new Mirror(), blockClass = 'rr-block', blockSelector = null, maskTextClass = 'rr-mask', @@ -1026,7 +1038,6 @@ function snapshot( iframeLoadTimeout, keepIframeSrcFn = () => false, } = options || {}; - const idNodeMap: idNodeMap = {}; const maskInputOptions: MaskInputOptions = maskAllInputs === true ? { @@ -1070,31 +1081,28 @@ function snapshot( : slimDOM === false ? {} : slimDOM; - return [ - serializeNodeWithId(n, { - doc: n, - map: idNodeMap, - blockClass, - blockSelector, - maskTextClass, - maskTextSelector, - skipChild: false, - inlineStylesheet, - maskInputOptions, - maskTextFn, - maskInputFn, - slimDOMOptions, - dataURLOptions, - inlineImages, - recordCanvas, - preserveWhiteSpace, - onSerialize, - onIframeLoad, - iframeLoadTimeout, - keepIframeSrcFn, - }), - idNodeMap, - ]; + return serializeNodeWithId(n, { + doc: n, + mirror, + blockClass, + blockSelector, + maskTextClass, + maskTextSelector, + skipChild: false, + inlineStylesheet, + maskInputOptions, + maskTextFn, + maskInputFn, + slimDOMOptions, + dataURLOptions, + inlineImages, + recordCanvas, + preserveWhiteSpace, + onSerialize, + onIframeLoad, + iframeLoadTimeout, + keepIframeSrcFn, + }); } export function visitSnapshot( diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index eedd49252f..89b4e1ac12 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -67,6 +67,7 @@ export type tagMap = { [key: string]: string; }; +// @deprecated export interface INode extends Node { __sn: serializedNodeWithId; } @@ -75,9 +76,9 @@ export interface ICanvas extends HTMLCanvasElement { __context: string; } -export type idNodeMap = { - [key: number]: INode; -}; +export type idNodeMap = Map; + +export type nodeMetaMap = WeakMap; export type MaskInputOptions = Partial<{ color: boolean; diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 7f84809929..5eba92f806 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -1,6 +1,12 @@ -import { INode, MaskInputFn, MaskInputOptions } from './types'; +import { + idNodeMap, + MaskInputFn, + MaskInputOptions, + nodeMetaMap, + serializedNodeWithId, +} from './types'; -export function isElement(n: Node | INode): n is Element { +export function isElement(n: Node): n is Element { return n.nodeType === n.ELEMENT_NODE; } @@ -9,6 +15,69 @@ export function isShadowRoot(n: Node): n is ShadowRoot { return Boolean(host && host.shadowRoot && host.shadowRoot === n); } +export class Mirror { + private idNodeMap: idNodeMap = new Map(); + private nodeMetaMap: nodeMetaMap = new WeakMap(); + + getId(n: Node | undefined | null): number { + if (!n) return -1; + + const id = this.getMeta(n)?.id; + + // if n is not a serialized Node, use -1 as its id. + return id ?? -1; + } + + getNode(id: number): Node | null { + return this.idNodeMap.get(id) || null; + } + + getIds(): number[] { + return Array.from(this.idNodeMap.keys()); + } + + getMeta(n: Node): serializedNodeWithId | null { + return this.nodeMetaMap.get(n) || null; + } + + // removes the node from idNodeMap + // doesn't remove the node from nodeMetaMap + removeNodeFromMap(n: Node) { + const id = this.getId(n); + this.idNodeMap.delete(id); + + if (n.childNodes) { + n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); + } + } + has(id: number): boolean { + return this.idNodeMap.has(id); + } + + hasNode(node: Node): boolean { + return this.nodeMetaMap.has(node); + } + + add(n: Node, meta: serializedNodeWithId) { + const id = meta.id; + this.idNodeMap.set(id, n); + this.nodeMetaMap.set(n, meta); + } + + replace(id: number, n: Node) { + this.idNodeMap.set(id, n); + } + + reset() { + this.idNodeMap = new Map(); + this.nodeMetaMap = new WeakMap(); + } +} + +export function createMirror(): Mirror { + return new Mirror(); +} + export function maskInputValue({ maskInputOptions, tagName, diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index 0d5af440b6..c34bfefcc1 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -131,8 +131,8 @@ describe('integration tests', function (this: ISuite) { const rebuildHtml = ( await page.evaluate(`${code} const x = new XMLSerializer(); - const [snap] = rrweb.snapshot(document); - let out = x.serializeToString(rrweb.rebuild(snap, { doc: document })[0]); + const snap = rrweb.snapshot(document); + let out = x.serializeToString(rrweb.rebuild(snap, { doc: document })); if (document.querySelector('html').getAttribute('xmlns') !== 'http://www.w3.org/1999/xhtml') { // this is just an artefact of serializeToString out = out.replace(' xmlns=\"http://www.w3.org/1999/xhtml\"', ''); @@ -168,7 +168,7 @@ describe('integration tests', function (this: ISuite) { `pre-check: images will be rendered ~326px high in BackCompat mode, and ~588px in CSS1Compat mode; getting: ${renderedHeight}px`, ); const rebuildRenderedHeight = await page.evaluate(`${code} -const [snap] = rrweb.snapshot(document); +const snap = rrweb.snapshot(document); const iframe = document.createElement('iframe'); iframe.setAttribute('width', document.body.clientWidth) iframe.setAttribute('height', document.body.clientHeight) @@ -205,9 +205,7 @@ iframe.contentDocument.querySelector('center').clientHeight inlineStylesheet: false })`); await page.waitFor(100); - const snapshot = await page.evaluate( - 'JSON.stringify(snapshot[0], null, 2);', - ); + const snapshot = await page.evaluate('JSON.stringify(snapshot, null, 2);'); assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); @@ -253,7 +251,7 @@ describe('iframe integration tests', function (this: ISuite) { }); const snapshotResult = JSON.stringify( await page.evaluate(`${code}; - rrweb.snapshot(document)[0]; + rrweb.snapshot(document); `), null, 2, @@ -302,7 +300,7 @@ describe('shadow DOM integration tests', function (this: ISuite) { }); const snapshotResult = JSON.stringify( await page.evaluate(`${code}; - rrweb.snapshot(document)[0]; + rrweb.snapshot(document); `), null, 2, diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index d1e94f9a82..5b05b7cafd 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -8,6 +8,7 @@ import { _isBlockedElement, } from '../src/snapshot'; import { serializedNodeWithId } from '../src/types'; +import { Mirror } from '../src/utils'; describe('absolute url to stylesheet', () => { const href = 'http://localhost/css/style.css'; @@ -139,7 +140,7 @@ describe('style elements', () => { const serializeNode = (node: Node): serializedNodeWithId | null => { return serializeNodeWithId(node, { doc: document, - map: {}, + mirror: new Mirror(), blockClass: 'blockblock', blockSelector: null, maskTextClass: 'maskmask', diff --git a/packages/rrweb-snapshot/typings/rebuild.d.ts b/packages/rrweb-snapshot/typings/rebuild.d.ts index 64a564cfd7..894149e2bb 100644 --- a/packages/rrweb-snapshot/typings/rebuild.d.ts +++ b/packages/rrweb-snapshot/typings/rebuild.d.ts @@ -1,19 +1,21 @@ -import { serializedNodeWithId, idNodeMap, INode, BuildCache } from './types'; +import { serializedNodeWithId, BuildCache } from './types'; +import { Mirror } from './utils'; export declare function addHoverClass(cssText: string, cache: BuildCache): string; export declare function createCache(): BuildCache; export declare function buildNodeWithSN(n: serializedNodeWithId, options: { doc: Document; - map: idNodeMap; + mirror: Mirror; skipChild?: boolean; hackCss: boolean; - afterAppend?: (n: INode) => unknown; + afterAppend?: (n: Node) => unknown; cache: BuildCache; -}): INode | null; +}): Node | null; declare function rebuild(n: serializedNodeWithId, options: { doc: Document; - onVisit?: (node: INode) => unknown; + onVisit?: (node: Node) => unknown; hackCss?: boolean; - afterAppend?: (n: INode) => unknown; + afterAppend?: (n: Node) => unknown; cache: BuildCache; -}): [Node | null, idNodeMap]; + mirror: Mirror; +}): Node | null; export default rebuild; diff --git a/packages/rrweb-snapshot/typings/snapshot.d.ts b/packages/rrweb-snapshot/typings/snapshot.d.ts index b970f751eb..0cdca439df 100644 --- a/packages/rrweb-snapshot/typings/snapshot.d.ts +++ b/packages/rrweb-snapshot/typings/snapshot.d.ts @@ -1,13 +1,14 @@ -import { serializedNodeWithId, INode, idNodeMap, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types'; +import { serializedNodeWithId, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn } from './types'; +import { Mirror } from './utils'; export declare const IGNORED_NODE = -2; export declare function absoluteToStylesheet(cssText: string | null, href: string): string; export declare function absoluteToDoc(doc: Document, attributeValue: string): string; export declare function transformAttribute(doc: Document, tagName: string, name: string, value: string): string; export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null): boolean; export declare function needMaskingText(node: Node | null, maskTextClass: string | RegExp, maskTextSelector: string | null): boolean; -export declare function serializeNodeWithId(n: Node | INode, options: { +export declare function serializeNodeWithId(n: Node, options: { doc: Document; - map: idNodeMap; + mirror: Mirror; blockClass: string | RegExp; blockSelector: string | null; maskTextClass: string | RegExp; @@ -23,11 +24,12 @@ export declare function serializeNodeWithId(n: Node | INode, options: { inlineImages?: boolean; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; + onSerialize?: (n: Node) => unknown; + onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedNodeWithId) => unknown; iframeLoadTimeout?: number; }): serializedNodeWithId | null; declare function snapshot(n: Document, options?: { + mirror?: Mirror; blockClass?: string | RegExp; blockSelector?: string | null; maskTextClass?: string | RegExp; @@ -41,11 +43,11 @@ declare function snapshot(n: Document, options?: { inlineImages?: boolean; recordCanvas?: boolean; preserveWhiteSpace?: boolean; - onSerialize?: (n: INode) => unknown; - onIframeLoad?: (iframeINode: INode, node: serializedNodeWithId) => unknown; + onSerialize?: (n: Node) => unknown; + onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedNodeWithId) => unknown; iframeLoadTimeout?: number; keepIframeSrcFn?: KeepIframeSrcFn; -}): [serializedNodeWithId | null, idNodeMap]; +}): serializedNodeWithId | null; export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void; export declare function cleanupSnapshot(): void; export default snapshot; diff --git a/packages/rrweb-snapshot/typings/types.d.ts b/packages/rrweb-snapshot/typings/types.d.ts index a8ccc0d305..9ffd0ea8ec 100644 --- a/packages/rrweb-snapshot/typings/types.d.ts +++ b/packages/rrweb-snapshot/typings/types.d.ts @@ -58,9 +58,8 @@ export interface INode extends Node { export interface ICanvas extends HTMLCanvasElement { __context: string; } -export declare type idNodeMap = { - [key: number]: INode; -}; +export declare type idNodeMap = Map; +export declare type nodeMetaMap = WeakMap; export declare type MaskInputOptions = Partial<{ color: boolean; date: boolean; diff --git a/packages/rrweb-snapshot/typings/utils.d.ts b/packages/rrweb-snapshot/typings/utils.d.ts index 6572ab8279..c3bf88f824 100644 --- a/packages/rrweb-snapshot/typings/utils.d.ts +++ b/packages/rrweb-snapshot/typings/utils.d.ts @@ -1,6 +1,21 @@ -import { INode, MaskInputFn, MaskInputOptions } from './types'; -export declare function isElement(n: Node | INode): n is Element; +import { MaskInputFn, MaskInputOptions, serializedNodeWithId } from './types'; +export declare function isElement(n: Node): n is Element; export declare function isShadowRoot(n: Node): n is ShadowRoot; +export declare class Mirror { + private idNodeMap; + private nodeMetaMap; + getId(n: Node | undefined | null): number; + getNode(id: number): Node | null; + getIds(): number[]; + getMeta(n: Node): serializedNodeWithId | null; + removeNodeFromMap(n: Node): void; + has(id: number): boolean; + hasNode(node: Node): boolean; + add(n: Node, meta: serializedNodeWithId): void; + replace(id: number, n: Node): void; + reset(): void; +} +export declare function createMirror(): Mirror; export declare function maskInputValue({ maskInputOptions, tagName, type, value, maskInputFn, }: { maskInputOptions: MaskInputOptions; tagName: string; diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 1825c1786f..e4331788d4 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -1,4 +1,4 @@ -import { serializedNodeWithId, INode } from 'rrweb-snapshot'; +import { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; import { mutationCallBack } from '../types'; export class IframeManager { @@ -18,11 +18,15 @@ export class IframeManager { this.loadListener = cb; } - public attachIframe(iframeEl: INode, childSn: serializedNodeWithId) { + public attachIframe( + iframeEl: HTMLIFrameElement, + childSn: serializedNodeWithId, + mirror: Mirror, + ) { this.mutationCb({ adds: [ { - parentId: iframeEl.__sn.id, + parentId: mirror.getId(iframeEl), nextId: null, node: childSn, }, @@ -32,6 +36,6 @@ export class IframeManager { attributes: [], isAttachIframe: true, }); - this.loadListener?.((iframeEl as unknown) as HTMLIFrameElement); + this.loadListener?.(iframeEl); } } diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 8c748a8214..3b6d910ceb 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -1,13 +1,17 @@ -import { snapshot, MaskInputOptions, SlimDOMOptions } from 'rrweb-snapshot'; +import { + snapshot, + MaskInputOptions, + SlimDOMOptions, + createMirror, +} from 'rrweb-snapshot'; import { initObservers, mutationBuffers } from './observer'; import { on, getWindowWidth, getWindowHeight, polyfill, - isIframeINode, hasShadowRoot, - createMirror, + isSerializedIframe, } from '../utils'; import { EventType, @@ -74,6 +78,9 @@ function record( sampling.mousemove = mousemoveWait; } + // reset mirror in case `record` this was called earlier + mirror.reset(); + const maskInputOptions: MaskInputOptions = maskAllInputs === true ? { @@ -252,7 +259,8 @@ function record( ); mutationBuffers.forEach((buf) => buf.lock()); // don't allow any mirror modifications during snapshotting - const [node, idNodeMap] = snapshot(document, { + const node = snapshot(document, { + mirror, blockClass, blockSelector, maskTextClass, @@ -264,18 +272,16 @@ function record( recordCanvas, inlineImages, onSerialize: (n) => { - if (isIframeINode(n)) { - iframeManager.addIframe(n); + if (isSerializedIframe(n, mirror)) { + iframeManager.addIframe(n as HTMLIFrameElement); } if (hasShadowRoot(n)) { shadowDomManager.addShadowRoot(n.shadowRoot, document); } }, onIframeLoad: (iframe, childSn) => { - iframeManager.attachIframe(iframe, childSn); - shadowDomManager.observeAttachShadow( - (iframe as Node) as HTMLIFrameElement, - ); + iframeManager.attachIframe(iframe, childSn, mirror); + shadowDomManager.observeAttachShadow(iframe); }, keepIframeSrcFn, }); @@ -284,7 +290,6 @@ function record( return console.warn('Failed to snapshot the document'); } - mirror.map = idNodeMap; wrappedEmit( wrapEvent({ type: EventType.FullSnapshot, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 1e4a8fbe07..7b8cd716ea 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -1,11 +1,11 @@ import { - INode, serializeNodeWithId, transformAttribute, IGNORED_NODE, isShadowRoot, needMaskingText, maskInputValue, + Mirror, } from 'rrweb-snapshot'; import { mutationRecord, @@ -13,7 +13,6 @@ import { attributeCursor, removedNodeMutation, addedNodeMutation, - Mirror, styleAttributeValue, observerParam, MutationBufferParam, @@ -23,8 +22,8 @@ import { isBlocked, isAncestorRemoved, isIgnored, - isIframeINode, hasShadowRoot, + isSerializedIframe, } from '../utils'; type DoubleLinkedListNode = { @@ -39,6 +38,7 @@ type NodeInLinkedList = Node & { function isNodeInLinkedList(n: Node | NodeInLinkedList): n is NodeInLinkedList { return '__ln' in n; } + class DoubleLinkedList { public length = 0; public head: DoubleLinkedListNode | null = null; @@ -117,9 +117,6 @@ class DoubleLinkedList { } const moveKey = (id: number, parentId: number) => `${id}@${parentId}`; -function isINode(n: Node | INode): n is INode { - return '__sn' in n; -} /** * controls behaviour of a MutationObserver @@ -255,7 +252,7 @@ export default class MutationBuffer { let nextId: number | null = IGNORED_NODE; // slimDOM: ignored while (nextId === IGNORED_NODE) { ns = ns && ns.nextSibling; - nextId = ns && this.mirror.getId((ns as unknown) as INode); + nextId = ns && this.mirror.getId(ns); } return nextId; }; @@ -277,15 +274,15 @@ export default class MutationBuffer { return; } const parentId = isShadowRoot(n.parentNode) - ? this.mirror.getId((shadowHost as unknown) as INode) - : this.mirror.getId((n.parentNode as Node) as INode); + ? this.mirror.getId(shadowHost) + : this.mirror.getId(n.parentNode); const nextId = getNextId(n); if (parentId === -1 || nextId === -1) { return addList.addNode(n); } let sn = serializeNodeWithId(n, { doc: this.doc, - map: this.mirror.map, + mirror: this.mirror, blockClass: this.blockClass, blockSelector: this.blockSelector, maskTextClass: this.maskTextClass, @@ -299,7 +296,7 @@ export default class MutationBuffer { recordCanvas: this.recordCanvas, inlineImages: this.inlineImages, onSerialize: (currentN) => { - if (isIframeINode(currentN)) { + if (isSerializedIframe(currentN, this.mirror)) { this.iframeManager.addIframe(currentN); } if (hasShadowRoot(n)) { @@ -307,10 +304,8 @@ export default class MutationBuffer { } }, onIframeLoad: (iframe, childSn) => { - this.iframeManager.attachIframe(iframe, childSn); - this.shadowDomManager.observeAttachShadow( - (iframe as Node) as HTMLIFrameElement, - ); + this.iframeManager.attachIframe(iframe, childSn, this.mirror); + this.shadowDomManager.observeAttachShadow(iframe); }, }); if (sn) { @@ -323,7 +318,7 @@ export default class MutationBuffer { }; while (this.mapRemoves.length) { - this.mirror.removeNodeFromMap(this.mapRemoves.shift() as INode); + this.mirror.removeNodeFromMap(this.mapRemoves.shift()!); } for (const n of this.movedSet) { @@ -353,9 +348,7 @@ export default class MutationBuffer { while (addList.length) { let node: DoubleLinkedListNode | null = null; if (candidate) { - const parentId = this.mirror.getId( - (candidate.value.parentNode as Node) as INode, - ); + const parentId = this.mirror.getId(candidate.value.parentNode); const nextId = getNextId(candidate.value); if (parentId !== -1 && nextId !== -1) { node = candidate; @@ -366,9 +359,7 @@ export default class MutationBuffer { const _node = addList.get(index)!; // ensure _node is defined before attempting to find value if (_node) { - const parentId = this.mirror.getId( - (_node.value.parentNode as Node) as INode, - ); + const parentId = this.mirror.getId(_node.value.parentNode); const nextId = getNextId(_node.value); if (parentId !== -1 && nextId !== -1) { node = _node; @@ -396,14 +387,14 @@ export default class MutationBuffer { const payload = { texts: this.texts .map((text) => ({ - id: this.mirror.getId(text.node as INode), + id: this.mirror.getId(text.node), value: text.value, })) // text mutation's id was not in the mirror map means the target node has been removed .filter((text) => this.mirror.has(text.id)), attributes: this.attributes .map((attribute) => ({ - id: this.mirror.getId(attribute.node as INode), + id: this.mirror.getId(attribute.node), attributes: attribute.attributes, })) // attribute mutation's id was not in the mirror map means the target node has been removed @@ -434,7 +425,7 @@ export default class MutationBuffer { }; private processMutation = (m: mutationRecord) => { - if (isIgnored(m.target)) { + if (isIgnored(m.target, this.mirror)) { return; } switch (m.type) { @@ -528,11 +519,14 @@ export default class MutationBuffer { case 'childList': { m.addedNodes.forEach((n) => this.genAdds(n, m.target)); m.removedNodes.forEach((n) => { - const nodeId = this.mirror.getId(n as INode); + const nodeId = this.mirror.getId(n); const parentId = isShadowRoot(m.target) - ? this.mirror.getId((m.target.host as unknown) as INode) - : this.mirror.getId(m.target as INode); - if (isBlocked(m.target, this.blockClass) || isIgnored(n)) { + ? this.mirror.getId(m.target.host) + : this.mirror.getId(m.target); + if ( + isBlocked(m.target, this.blockClass) || + isIgnored(n, this.mirror) + ) { return; } // removed node has not been serialized yet, just remove it from the Set @@ -547,7 +541,7 @@ export default class MutationBuffer { * newly added node will be serialized without child nodes. * TODO: verify this */ - } else if (isAncestorRemoved(m.target as INode, this.mirror)) { + } else if (isAncestorRemoved(m.target, this.mirror)) { /** * If parent id was not in the mirror map any more, it * means the parent node has already been removed. So @@ -575,22 +569,23 @@ export default class MutationBuffer { } }; - private genAdds = (n: Node | INode, target?: Node | INode) => { + private genAdds = (n: Node, target?: Node) => { // parent was blocked, so we can ignore this node if (target && isBlocked(target, this.blockClass)) { return; } - if (isINode(n)) { - if (isIgnored(n)) { + + if (this.mirror.getMeta(n)) { + if (isIgnored(n, this.mirror)) { return; } this.movedSet.add(n); let targetId: number | null = null; - if (target && isINode(target)) { - targetId = target.__sn.id; + if (target && this.mirror.getMeta(target)) { + targetId = this.mirror.getId(target); } - if (targetId) { - this.movedMap[moveKey(n.__sn.id, targetId)] = true; + if (targetId && targetId !== -1) { + this.movedMap[moveKey(this.mirror.getId(n), targetId)] = true; } } else { this.addedSet.add(n); @@ -600,7 +595,7 @@ export default class MutationBuffer { // if this node is blocked `serializeNode` will turn it into a placeholder element // but we have to remove it's children otherwise they will be added as placeholders too if (!isBlocked(n, this.blockClass)) - n.childNodes.forEach((childN) => this.genAdds(childN)); + (n as Node).childNodes.forEach((childN) => this.genAdds(childN)); }; } @@ -624,7 +619,7 @@ function isParentRemoved( if (!parentNode) { return false; } - const parentId = mirror.getId((parentNode as Node) as INode); + const parentId = mirror.getId(parentNode); if (removes.some((r) => r.id === parentId)) { return true; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index a735c93e3c..5504377853 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -1,4 +1,4 @@ -import { INode, MaskInputOptions, maskInputValue } from 'rrweb-snapshot'; +import { MaskInputOptions, maskInputValue } from 'rrweb-snapshot'; import { FontFaceSet } from 'css-font-loading-module'; import { throttle, @@ -173,7 +173,7 @@ function initMoveObserver({ positions.push({ x: clientX, y: clientY, - id: mirror.getId(target as INode), + id: mirror.getId(target as Node), timeOffset: Date.now() - timeBaseline, }); // it is possible DragEvent is undefined even on devices @@ -221,14 +221,14 @@ function initMouseInteractionObserver({ const getHandler = (eventKey: keyof typeof MouseInteractions) => { return (event: MouseEvent | TouchEvent) => { const target = getEventTarget(event) as Node; - if (isBlocked(target as Node, blockClass)) { + if (isBlocked(target, blockClass)) { return; } const e = isTouchEvent(event) ? event.changedTouches[0] : event; if (!e) { return; } - const id = mirror.getId(target as INode); + const id = mirror.getId(target); const { clientX, clientY } = e; mouseInteractionCb({ type: MouseInteractions[eventKey], @@ -270,7 +270,7 @@ export function initScrollObserver({ if (!target || isBlocked(target as Node, blockClass)) { return; } - const id = mirror.getId(target as INode); + const id = mirror.getId(target as Node); if (target === doc) { const scrollEl = (doc.scrollingElement || doc.documentElement)!; scrollCb({ @@ -408,7 +408,7 @@ function initInputObserver({ lastInputValue.isChecked !== v.isChecked ) { lastInputValueMap.set(target, v); - const id = mirror.getId(target as INode); + const id = mirror.getId(target as Node); inputCb({ ...v, id, @@ -497,7 +497,7 @@ function initStyleSheetObserver( rule: string, index?: number, ) { - const id = mirror.getId(this.ownerNode as INode); + const id = mirror.getId(this.ownerNode); if (id !== -1) { styleSheetRuleCb({ id, @@ -509,7 +509,7 @@ function initStyleSheetObserver( const deleteRule = win.CSSStyleSheet.prototype.deleteRule; win.CSSStyleSheet.prototype.deleteRule = function (index: number) { - const id = mirror.getId(this.ownerNode as INode); + const id = mirror.getId(this.ownerNode); if (id !== -1) { styleSheetRuleCb({ id, @@ -554,7 +554,7 @@ function initStyleSheetObserver( }; type.prototype.insertRule = function (rule: string, index?: number) { - const id = mirror.getId(this.parentStyleSheet.ownerNode as INode); + const id = mirror.getId(this.parentStyleSheet.ownerNode); if (id !== -1) { styleSheetRuleCb({ id, @@ -573,7 +573,7 @@ function initStyleSheetObserver( }; type.prototype.deleteRule = function (index: number) { - const id = mirror.getId(this.parentStyleSheet.ownerNode as INode); + const id = mirror.getId(this.parentStyleSheet.ownerNode); if (id !== -1) { styleSheetRuleCb({ id, @@ -605,9 +605,7 @@ function initStyleDeclarationObserver( value: string, priority: string, ) { - const id = mirror.getId( - (this.parentRule?.parentStyleSheet?.ownerNode as unknown) as INode, - ); + const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); if (id !== -1) { styleDeclarationCb({ id, @@ -627,9 +625,7 @@ function initStyleDeclarationObserver( this: CSSStyleDeclaration, property: string, ) { - const id = mirror.getId( - (this.parentRule?.parentStyleSheet?.ownerNode as unknown) as INode, - ); + const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); if (id !== -1) { styleDeclarationCb({ id, @@ -663,7 +659,7 @@ function initMediaInteractionObserver({ const { currentTime, volume, muted } = target as HTMLMediaElement; mediaInteractionCb({ type, - id: mirror.getId(target as INode), + id: mirror.getId(target as Node), currentTime, volume, muted, diff --git a/packages/rrweb/src/record/observers/canvas/2d.ts b/packages/rrweb/src/record/observers/canvas/2d.ts index dd63469b1e..6e7049af82 100644 --- a/packages/rrweb/src/record/observers/canvas/2d.ts +++ b/packages/rrweb/src/record/observers/canvas/2d.ts @@ -1,11 +1,10 @@ -import { INode } from 'rrweb-snapshot'; +import { Mirror } from 'rrweb-snapshot'; import { blockClass, CanvasContext, canvasManagerMutationCallback, IWindow, listenerHandler, - Mirror, } from '../../../types'; import { hookSetter, isBlocked, patch } from '../../../utils'; @@ -36,7 +35,7 @@ export default function initCanvas2DMutationObserver( this: CanvasRenderingContext2D, ...args: Array ) { - if (!isBlocked((this.canvas as unknown) as INode, blockClass)) { + if (!isBlocked(this.canvas, blockClass)) { // Using setTimeout as getImageData + JSON.stringify can be heavy // and we'd rather not block the main thread setTimeout(() => { diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index a266e5fb73..007419b37d 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -1,4 +1,4 @@ -import { INode } from 'rrweb-snapshot'; +import { Mirror } from 'rrweb-snapshot'; import { blockClass, canvasManagerMutationCallback, @@ -7,7 +7,6 @@ import { canvasMutationWithType, IWindow, listenerHandler, - Mirror, } from '../../../types'; import initCanvas2DMutationObserver from './2d'; import initCanvasContextObserver from './canvas'; @@ -126,7 +125,7 @@ export class CanvasManager { flushPendingCanvasMutations() { this.pendingCanvasMutations.forEach( (values: canvasMutationCommand[], canvas: HTMLCanvasElement) => { - const id = this.mirror.getId((canvas as unknown) as INode); + const id = this.mirror.getId(canvas); this.flushPendingCanvasMutationFor(canvas, id); }, ); diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index 437af7d5f3..ff42c7a05d 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -1,4 +1,4 @@ -import { INode, ICanvas } from 'rrweb-snapshot'; +import { ICanvas } from 'rrweb-snapshot'; import { blockClass, IWindow, listenerHandler } from '../../../types'; import { isBlocked, patch } from '../../../utils'; @@ -17,7 +17,7 @@ export default function initCanvasContextObserver( contextType: string, ...args: Array ) { - if (!isBlocked((this as unknown) as INode, blockClass)) { + if (!isBlocked(this, blockClass)) { if (!('__context' in this)) (this as ICanvas).__context = contextType; } diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index 45b03928f3..1ebe2e19da 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -1,4 +1,4 @@ -import { INode } from 'rrweb-snapshot'; +import { Mirror } from 'rrweb-snapshot'; import { blockClass, CanvasContext, @@ -6,7 +6,6 @@ import { canvasMutationWithType, IWindow, listenerHandler, - Mirror, } from '../../../types'; import { hookSetter, isBlocked, patch } from '../../../utils'; import { saveWebGLVar, serializeArgs } from './serialize-args'; @@ -32,8 +31,8 @@ function patchGLPrototype( return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); saveWebGLVar(result, win, prototype); - if (!isBlocked((this.canvas as unknown) as INode, blockClass)) { - const id = mirror.getId((this.canvas as unknown) as INode); + if (!isBlocked(this.canvas, blockClass)) { + const id = mirror.getId(this.canvas); const recordArgs = serializeArgs([...args], win, prototype); const mutation: canvasMutationWithType = { diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index e0ad26f33c..1bc50d0f2e 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -1,12 +1,12 @@ import { mutationCallBack, - Mirror, scrollCallback, MutationBufferParam, SamplingStrategy, } from '../types'; import { initMutationObserver, initScrollObserver } from './observer'; import { patch } from '../utils'; +import { Mirror } from 'rrweb-snapshot'; type BypassOptions = Omit< MutationBufferParam, diff --git a/packages/rrweb/src/replay/canvas/2d.ts b/packages/rrweb/src/replay/canvas/2d.ts index b9fde639b3..feeb94d8c2 100644 --- a/packages/rrweb/src/replay/canvas/2d.ts +++ b/packages/rrweb/src/replay/canvas/2d.ts @@ -15,7 +15,7 @@ export default function canvasMutation({ errorHandler: Replayer['warnCanvasMutationFailed']; }): void { try { - const ctx = ((target as unknown) as HTMLCanvasElement).getContext('2d')!; + const ctx = target.getContext('2d')!; if (mutation.setter) { // skip some read-only type checks diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index df567b3ae1..d7027a14bf 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1,10 +1,11 @@ import { rebuild, buildNodeWithSN, - INode, NodeType, BuildCache, createCache, + Mirror, + createMirror, } from 'rrweb-snapshot'; import * as mittProxy from 'mitt'; import { polyfill as smoothscrollPolyfill } from './smoothscroll'; @@ -33,7 +34,6 @@ import { scrollData, inputData, canvasMutationData, - Mirror, ElementState, styleAttributeValue, styleValueWithPriority, @@ -43,15 +43,14 @@ import { textMutation, } from '../types'; import { - createMirror, polyfill, TreeIndex, queueToResolveTrees, iterateResolveTree, AppendedIframe, - isIframeINode, getBaseDimension, hasShadowRoot, + isSerializedIframe, } from '../utils'; import getInjectStyleRules from './styles/inject-style'; import './styles/style.css'; @@ -115,8 +114,8 @@ export class Replayer { private legacy_missingNodeRetryMap: missingNodeMap = {}; private treeIndex!: TreeIndex; - private fragmentParentMap!: Map; - private elementStateMap!: Map; + private fragmentParentMap!: Map; + private elementStateMap!: Map; // Hold the list of CSSRules for in-memory state restoration private virtualStyleRulesMap!: VirtualStyleRulesMap; @@ -167,8 +166,8 @@ export class Replayer { this.setupDom(); this.treeIndex = new TreeIndex(); - this.fragmentParentMap = new Map(); - this.elementStateMap = new Map(); + this.fragmentParentMap = new Map(); + this.elementStateMap = new Map(); this.virtualStyleRulesMap = new Map(); this.emitter.on(ReplayerEvents.Flush, () => { @@ -657,13 +656,14 @@ export class Replayer { } this.legacy_missingNodeRetryMap = {}; const collected: AppendedIframe[] = []; - this.mirror.map = rebuild(event.data.node, { + rebuild(event.data.node, { doc: this.iframe.contentDocument, afterAppend: (builtNode) => { this.collectIframeAndAttachDocument(collected, builtNode); }, cache: this.cache, - })[1]; + mirror: this.mirror, + }); for (const { mutationInQueue, builtNode } of collected) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( @@ -715,8 +715,8 @@ export class Replayer { let parent = iframeEl.parentNode; while (parent) { // The parent of iframeEl is virtual parent and we need to mount it on the dom. - if (this.fragmentParentMap.has((parent as unknown) as INode)) { - const frag = (parent as unknown) as INode; + if (this.fragmentParentMap.has(parent)) { + const frag = parent; const realParent = this.fragmentParentMap.get(frag)!; this.restoreRealParent(frag, realParent); break; @@ -726,14 +726,15 @@ export class Replayer { } buildNodeWithSN(mutation.node, { doc: iframeEl.contentDocument!, - map: this.mirror.map, + mirror: this.mirror, hackCss: true, skipChild: false, afterAppend: (builtNode) => { this.collectIframeAndAttachDocument(collected, builtNode); + const sn = this.mirror.getMeta(builtNode); if ( - builtNode.__sn.type === NodeType.Element && - builtNode.__sn.tagName.toUpperCase() === 'HTML' + sn?.type === NodeType.Element && + sn?.tagName.toUpperCase() === 'HTML' ) { const { documentElement, head } = iframeEl.contentDocument!; this.insertStyleRules(documentElement, head); @@ -751,11 +752,11 @@ export class Replayer { private collectIframeAndAttachDocument( collected: AppendedIframe[], - builtNode: INode, + builtNode: Node, ) { - if (isIframeINode(builtNode)) { + if (isSerializedIframe(builtNode, this.mirror)) { const mutationInQueue = this.newDocumentQueue.find( - (m) => m.parentId === builtNode.__sn.id, + (m) => m.parentId === this.mirror.getId(builtNode), ); if (mutationInQueue) { collected.push({ mutationInQueue, builtNode }); @@ -905,7 +906,7 @@ export class Replayer { d.adds.forEach((m) => this.treeIndex.add(m)); d.texts.forEach((m) => { const target = this.mirror.getNode(m.id); - const parent = (target?.parentNode as unknown) as INode | null; + const parent = target?.parentNode; // remove any style rules that pending // for stylesheets where the contents get replaced if (parent && this.virtualStyleRulesMap.has(parent)) @@ -973,13 +974,13 @@ export class Replayer { const { triggerFocus } = this.config; switch (d.type) { case MouseInteractions.Blur: - if ('blur' in ((target as Node) as HTMLElement)) { - ((target as Node) as HTMLElement).blur(); + if ('blur' in (target as HTMLElement)) { + (target as HTMLElement).blur(); } break; case MouseInteractions.Focus: - if (triggerFocus && ((target as Node) as HTMLElement).focus) { - ((target as Node) as HTMLElement).focus({ + if (triggerFocus && (target as HTMLElement).focus) { + (target as HTMLElement).focus({ preventScroll: true, }); } @@ -1080,7 +1081,7 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - const mediaEl = (target as Node) as HTMLMediaElement; + const mediaEl = target as HTMLMediaElement; try { if (d.currentTime) { mediaEl.currentTime = d.currentTime; @@ -1116,8 +1117,8 @@ export class Replayer { return this.debugNodeNotFound(d, d.id); } - const styleEl = (target as Node) as HTMLStyleElement; - const parent = (target.parentNode as unknown) as INode; + const styleEl = target as HTMLStyleElement; + const parent = target.parentNode!; const usingVirtualParent = this.fragmentParentMap.has(parent); /** @@ -1218,8 +1219,8 @@ export class Replayer { return this.debugNodeNotFound(d, d.id); } - const styleEl = (target as Node) as HTMLStyleElement; - const parent = (target.parentNode as unknown) as INode; + const styleEl = target as HTMLStyleElement; + const parent = target.parentNode!; const usingVirtualParent = this.fragmentParentMap.has(parent); const styleSheet = usingVirtualParent ? null : styleEl.sheet; @@ -1279,7 +1280,7 @@ export class Replayer { canvasMutation({ event: e, mutation: d, - target: (target as unknown) as HTMLCanvasElement, + target: target as HTMLCanvasElement, imageMap: this.imageMap, errorHandler: this.warnCanvasMutationFailed.bind(this), }); @@ -1318,7 +1319,7 @@ export class Replayer { if (this.virtualStyleRulesMap.has(target)) { this.virtualStyleRulesMap.delete(target); } - let parent: INode | null | ShadowRoot = this.mirror.getNode( + let parent: Node | null | ShadowRoot = this.mirror.getNode( mutation.parentId, ); if (!parent) { @@ -1331,8 +1332,9 @@ export class Replayer { this.mirror.removeNodeFromMap(target); if (parent) { let realTarget = null; - const realParent = - '__sn' in parent ? this.fragmentParentMap.get(parent) : undefined; + const realParent = this.mirror.getMeta(parent) + ? this.fragmentParentMap.get(parent) + : undefined; if (realParent && realParent.contains(target)) { parent = realParent; } else if (this.fragmentParentMap.has(target)) { @@ -1373,7 +1375,7 @@ export class Replayer { const nextNotInDOM = (mutation: addedNodeMutation) => { let next: Node | null = null; if (mutation.nextId) { - next = this.mirror.getNode(mutation.nextId) as Node; + next = this.mirror.getNode(mutation.nextId); } // next not present at this moment if ( @@ -1391,7 +1393,7 @@ export class Replayer { if (!this.iframe.contentDocument) { return console.warn('Looks like your replayer has been destroyed.'); } - let parent: INode | null | ShadowRoot = this.mirror.getNode( + let parent: Node | null | ShadowRoot = this.mirror.getNode( mutation.parentId, ); if (!parent) { @@ -1412,20 +1414,19 @@ export class Replayer { } const hasIframeChild = - ((parent as unknown) as HTMLElement).getElementsByTagName?.('iframe') - .length > 0; + (parent as HTMLElement).getElementsByTagName?.('iframe').length > 0; /** - * Why !isIframeINode(parent)? If parent element is an iframe, iframe document can't be appended to virtual parent. + * Why !isSerializedIframe(parent)? If parent element is an iframe, iframe document can't be appended to virtual parent. * Why !hasIframeChild? If we move iframe elements from dom to fragment document, we will lose the contentDocument of iframe. So we need to disable the virtual dom optimization if a parent node contains iframe elements. */ if ( useVirtualParent && parentInDocument && - !isIframeINode(parent) && + !isSerializedIframe(parent, this.mirror) && !hasIframeChild ) { - const virtualParent = (document.createDocumentFragment() as unknown) as INode; - this.mirror.map[mutation.parentId] = virtualParent; + const virtualParent = document.createDocumentFragment(); + this.mirror.replace(mutation.parentId, virtualParent); this.fragmentParentMap.set(virtualParent, parent); // store the state, like scroll position, of child nodes before they are unmounted from dom @@ -1440,18 +1441,18 @@ export class Replayer { 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)) { - ((parent as Node) as HTMLElement).attachShadow({ mode: 'open' }); - parent = ((parent as Node) as HTMLElement).shadowRoot!; + (parent as HTMLElement).attachShadow({ mode: 'open' }); + parent = (parent as HTMLElement).shadowRoot!; } else parent = parent.shadowRoot; } let previous: Node | null = null; let next: Node | null = null; if (mutation.previousId) { - previous = this.mirror.getNode(mutation.previousId) as Node; + previous = this.mirror.getNode(mutation.previousId); } if (mutation.nextId) { - next = this.mirror.getNode(mutation.nextId) as Node; + next = this.mirror.getNode(mutation.nextId); } if (nextNotInDOM(mutation)) { return queue.push(mutation); @@ -1464,17 +1465,17 @@ export class Replayer { const targetDoc = mutation.node.rootId ? this.mirror.getNode(mutation.node.rootId) : this.iframe.contentDocument; - if (isIframeINode(parent)) { + if (isSerializedIframe(parent, this.mirror)) { this.attachDocumentToIframe(mutation, parent); return; } const target = buildNodeWithSN(mutation.node, { doc: targetDoc as Document, - map: this.mirror.map, + mirror: this.mirror, skipChild: true, hackCss: true, cache: this.cache, - }) as INode; + })!; // legacy data, we should not have -1 siblings any more if (mutation.previousId === -1 || mutation.nextId === -1) { @@ -1485,10 +1486,11 @@ export class Replayer { return; } + const parentSn = this.mirror.getMeta(parent); if ( - '__sn' in parent && - parent.__sn.type === NodeType.Element && - parent.__sn.tagName === 'textarea' && + parentSn && + parentSn.type === NodeType.Element && + parentSn.tagName === 'textarea' && mutation.node.type === NodeType.Text ) { // https://github.com/rrweb-io/rrweb/issues/745 @@ -1521,9 +1523,10 @@ export class Replayer { parent.appendChild(target); } - if (isIframeINode(target)) { + if (isSerializedIframe(target, this.mirror)) { + const targetId = this.mirror.getId(target); const mutationInQueue = this.newDocumentQueue.find( - (m) => m.parentId === target.__sn.id, + (m) => m.parentId === targetId, ); if (mutationInQueue) { this.attachDocumentToIframe(mutationInQueue, target); @@ -1611,10 +1614,10 @@ export class Replayer { if (typeof attributeName === 'string') { const value = mutation.attributes[attributeName]; if (value === null) { - ((target as Node) as Element).removeAttribute(attributeName); + (target as Element).removeAttribute(attributeName); } else if (typeof value === 'string') { try { - ((target as Node) as Element).setAttribute(attributeName, value); + (target as Element).setAttribute(attributeName, value); } catch (error) { if (this.config.showWarning) { console.warn( @@ -1625,7 +1628,7 @@ export class Replayer { } } else if (attributeName === 'style') { let styleValues = value as styleAttributeValue; - const targetEl = (target as Node) as HTMLElement; + const targetEl = target as HTMLElement; for (var s in styleValues) { if (styleValues[s] === false) { targetEl.style.removeProperty(s); @@ -1654,23 +1657,24 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - if ((target as Node) === this.iframe.contentDocument) { + const sn = this.mirror.getMeta(target); + if (target === this.iframe.contentDocument) { this.iframe.contentWindow!.scrollTo({ top: d.y, left: d.x, behavior: isSync ? 'auto' : 'smooth', }); - } else if (target.__sn.type === NodeType.Document) { + } else if (sn?.type === NodeType.Document) { // nest iframe content document - ((target as unknown) as Document).defaultView!.scrollTo({ + (target as Document).defaultView!.scrollTo({ top: d.y, left: d.x, behavior: isSync ? 'auto' : 'smooth', }); } else { try { - ((target as Node) as Element).scrollTop = d.y; - ((target as Node) as Element).scrollLeft = d.x; + (target as Element).scrollTop = d.y; + (target as Element).scrollLeft = d.x; } catch (error) { /** * Seldomly we may found scroll target was removed before @@ -1686,8 +1690,8 @@ export class Replayer { return this.debugNodeNotFound(d, d.id); } try { - ((target as Node) as HTMLInputElement).checked = d.isChecked; - ((target as Node) as HTMLInputElement).value = d.text; + (target as HTMLInputElement).checked = d.isChecked; + (target as HTMLInputElement).value = d.text; } catch (error) { // for safe } @@ -1699,7 +1703,7 @@ export class Replayer { return this.debugNodeNotFound(mutation, d.id); } try { - ((target as Node) as HTMLElement).textContent = d.value; + (target as HTMLElement).textContent = d.value; } catch (error) { // for safe } @@ -1720,7 +1724,7 @@ export class Replayer { delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; if (mutation.previousId || mutation.nextId) { - this.legacy_resolveMissingNode(map, parent, node as Node, mutation); + this.legacy_resolveMissingNode(map, parent, node, mutation); } } if (nextInMap) { @@ -1729,7 +1733,7 @@ export class Replayer { delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; if (mutation.previousId || mutation.nextId) { - this.legacy_resolveMissingNode(map, parent, node as Node, mutation); + this.legacy_resolveMissingNode(map, parent, node, mutation); } } } @@ -1755,7 +1759,7 @@ export class Replayer { if (!isSync) { this.drawMouseTail({ x: _x, y: _y }); } - this.hoverElements((target as Node) as Element); + this.hoverElements(target as Element); } private drawMouseTail(position: { x: number; y: number }) { @@ -1835,18 +1839,21 @@ export class Replayer { * @param frag fragment document, the virtual parent * @param parent real parent element */ - private restoreRealParent(frag: INode, parent: INode) { - this.mirror.map[parent.__sn.id] = parent; + private restoreRealParent(frag: Node, parent: Node) { + const id = this.mirror.getId(frag); + const parentSn = this.mirror.getMeta(parent); + this.mirror.replace(id, parent); + /** * If we have already set value attribute on textarea, * then we could not apply text content as default value any more. */ if ( - parent.__sn.type === NodeType.Element && - parent.__sn.tagName === 'textarea' && + parentSn?.type === NodeType.Element && + parentSn?.tagName === 'textarea' && frag.textContent ) { - ((parent as unknown) as HTMLTextAreaElement).value = frag.textContent; + (parent as HTMLTextAreaElement).value = frag.textContent; } parent.appendChild(frag); // restore state of elements after they are mounted @@ -1858,10 +1865,10 @@ export class Replayer { * the state should be restored in the handler of event ReplayerEvents.Flush * e.g. browser would lose scroll position after the process that we add children of parent node to Fragment Document as virtual dom */ - private storeState(parent: INode) { + private storeState(parent: Node) { if (parent) { if (parent.nodeType === parent.ELEMENT_NODE) { - const parentElement = (parent as unknown) as HTMLElement; + const parentElement = parent as HTMLElement; if (parentElement.scrollLeft || parentElement.scrollTop) { // store scroll position state this.elementStateMap.set(parent, { @@ -1875,7 +1882,7 @@ export class Replayer { ); const children = parentElement.children; for (const child of Array.from(children)) { - this.storeState((child as unknown) as INode); + this.storeState(child); } } } @@ -1885,9 +1892,9 @@ export class Replayer { * restore the state of elements recursively, which was stored before elements were unmounted from dom in virtual parent mode * this function corresponds to function storeState */ - private restoreState(parent: INode) { + private restoreState(parent: Node) { if (parent.nodeType === parent.ELEMENT_NODE) { - const parentElement = (parent as unknown) as HTMLElement; + const parentElement = parent as HTMLElement; if (this.elementStateMap.has(parent)) { const storedState = this.elementStateMap.get(parent)!; // restore scroll position @@ -1899,12 +1906,12 @@ export class Replayer { } const children = parentElement.children; for (const child of Array.from(children)) { - this.restoreState((child as unknown) as INode); + this.restoreState(child); } } } - private restoreNodeSheet(node: INode) { + private restoreNodeSheet(node: Node) { const storedRules = this.virtualStyleRulesMap.get(node); if (node.nodeName !== 'STYLE') { return; @@ -1914,7 +1921,7 @@ export class Replayer { return; } - const styleNode = (node as unknown) as HTMLStyleElement; + const styleNode = node as HTMLStyleElement; applyVirtualStyleRulesToNode(storedRules, styleNode); } diff --git a/packages/rrweb/src/replay/virtual-styles.ts b/packages/rrweb/src/replay/virtual-styles.ts index f850df27c9..ca13344af2 100644 --- a/packages/rrweb/src/replay/virtual-styles.ts +++ b/packages/rrweb/src/replay/virtual-styles.ts @@ -1,5 +1,3 @@ -import { INode } from 'rrweb-snapshot'; - export enum StyleRuleType { Insert, Remove, @@ -37,7 +35,7 @@ type RemovePropertyRule = { export type VirtualStyleRules = Array< InsertRule | RemoveRule | SnapshotRule | SetPropertyRule | RemovePropertyRule >; -export type VirtualStyleRulesMap = Map; +export type VirtualStyleRulesMap = Map; export function getNestedRule( rules: CSSRuleList, @@ -173,7 +171,7 @@ export function storeCSSRules( const cssTexts = Array.from( (parentElement as HTMLStyleElement).sheet?.cssRules || [], ).map((rule) => rule.cssText); - virtualStyleRulesMap.set((parentElement as unknown) as INode, [ + virtualStyleRulesMap.set(parentElement, [ { type: StyleRuleType.Snapshot, cssTexts, diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index dea29982dc..acf02173aa 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -1,6 +1,6 @@ import { serializedNodeWithId, - idNodeMap, + Mirror, INode, MaskInputOptions, SlimDOMOptions, @@ -571,11 +571,13 @@ export type DocumentDimension = { absoluteScale: number; }; -export type Mirror = { - map: idNodeMap; - getId: (n: INode) => number; +export type DeprecatedMirror = { + map: { + [key: number]: INode; + }; + getId: (n: Node) => number; getNode: (id: number) => INode | null; - removeNodeFromMap: (n: INode) => void; + removeNodeFromMap: (n: Node) => void; has: (id: number) => boolean; reset: () => void; }; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 91376444ef..ebb212162b 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -1,5 +1,4 @@ import { - Mirror, throttleOptions, listenerHandler, hookResetter, @@ -14,14 +13,9 @@ import { inputData, DocumentDimension, IWindow, + DeprecatedMirror, } from './types'; -import { - INode, - IGNORED_NODE, - serializedNodeWithId, - NodeType, - isShadowRoot, -} from 'rrweb-snapshot'; +import { Mirror, IGNORED_NODE, isShadowRoot } from 'rrweb-snapshot'; export function on( type: string, @@ -33,38 +27,6 @@ export function on( return () => target.removeEventListener(type, fn, options); } -export function createMirror(): Mirror { - return { - map: {}, - getId(n) { - // if n is not a serialized INode, use -1 as its id. - if (!n || !n.__sn) { - return -1; - } - return n.__sn.id; - }, - getNode(id) { - return this.map[id] || null; - }, - // TODO: use a weakmap to get rid of manually memory management - removeNodeFromMap(n) { - const id = n.__sn && n.__sn.id; - delete this.map[id]; - if (n.childNodes) { - n.childNodes.forEach((child) => - this.removeNodeFromMap((child as Node) as INode), - ); - } - }, - has(id) { - return this.map.hasOwnProperty(id); - }, - reset() { - this.map = {}; - }, - }; -} - // https://github.com/rrweb-io/rrweb/pull/407 const DEPARTED_MIRROR_ACCESS_WARNING = 'Please stop import mirror directly. Instead of that,' + @@ -72,7 +34,7 @@ const DEPARTED_MIRROR_ACCESS_WARNING = 'now you can use replayer.getMirror() to access the mirror instance of a replayer,' + '\r\n' + 'or you can use record.mirror to access the mirror instance during recording.'; -export let _mirror: Mirror = { +export let _mirror: DeprecatedMirror = { map: {}, getId() { console.error(DEPARTED_MIRROR_ACCESS_WARNING); @@ -251,16 +213,13 @@ export function isBlocked(node: Node | null, blockClass: blockClass): boolean { return isBlocked(node.parentNode, blockClass); } -export function isIgnored(n: Node | INode): boolean { - if ('__sn' in n) { - return (n as INode).__sn.id === IGNORED_NODE; - } +export function isIgnored(n: Node, mirror: Mirror): boolean { // The main part of the slimDOM check happens in // rrweb-snapshot::serializeNodeWithId - return false; + return mirror.getId(n) === IGNORED_NODE; } -export function isAncestorRemoved(target: INode, mirror: Mirror): boolean { +export function isAncestorRemoved(target: Node, mirror: Mirror): boolean { if (isShadowRoot(target)) { return false; } @@ -278,7 +237,7 @@ export function isAncestorRemoved(target: INode, mirror: Mirror): boolean { if (!target.parentNode) { return true; } - return isAncestorRemoved((target.parentNode as unknown) as INode, mirror); + return isAncestorRemoved(target.parentNode, mirror); } export function isTouchEvent( @@ -325,6 +284,7 @@ export type TreeNode = { texts: textMutation[]; attributes: attributeMutation[]; }; + export class TreeIndex { public tree!: Record; @@ -363,12 +323,12 @@ export class TreeIndex { const treeNode = this.indexes.get(mutation.id); const deepRemoveFromMirror = (id: number) => { + if (id === -1) return; + this.removeIdSet.add(id); const node = mirror.getNode(id); node?.childNodes.forEach((childNode) => { - if ('__sn' in childNode) { - deepRemoveFromMirror(((childNode as unknown) as INode).__sn.id); - } + deepRemoveFromMirror(mirror.getId(childNode)); }); }; const deepRemoveFromTreeIndex = (node: TreeNode) => { @@ -576,24 +536,16 @@ export function iterateResolveTree( } } -type HTMLIFrameINode = HTMLIFrameElement & { - __sn: serializedNodeWithId; -}; export type AppendedIframe = { mutationInQueue: addedNodeMutation; - builtNode: HTMLIFrameINode; + builtNode: HTMLIFrameElement; }; -export function isIframeINode( - node: INode | ShadowRoot, -): node is HTMLIFrameINode { - if ('__sn' in node) { - return ( - node.__sn.type === NodeType.Element && node.__sn.tagName === 'iframe' - ); - } - // node can be document fragment when using the virtual parent feature - return false; +export function isSerializedIframe( + n: Node, + mirror: Mirror, +): n is HTMLIFrameElement { + return Boolean(n.nodeName === 'IFRAME' && mirror.getMeta(n)); } export function getBaseDimension( diff --git a/packages/rrweb/typings/record/iframe-manager.d.ts b/packages/rrweb/typings/record/iframe-manager.d.ts index 4300a7abce..9fbf136cc4 100644 --- a/packages/rrweb/typings/record/iframe-manager.d.ts +++ b/packages/rrweb/typings/record/iframe-manager.d.ts @@ -1,4 +1,4 @@ -import { serializedNodeWithId, INode } from 'rrweb-snapshot'; +import { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; import { mutationCallBack } from '../types'; export declare class IframeManager { private iframes; @@ -9,5 +9,5 @@ export declare class IframeManager { }); addIframe(iframeEl: HTMLIFrameElement): void; addLoadListener(cb: (iframeEl: HTMLIFrameElement) => unknown): void; - attachIframe(iframeEl: INode, childSn: serializedNodeWithId): void; + attachIframe(iframeEl: HTMLIFrameElement, childSn: serializedNodeWithId, mirror: Mirror): void; } diff --git a/packages/rrweb/typings/record/index.d.ts b/packages/rrweb/typings/record/index.d.ts index c4f541c887..f997da1f6f 100644 --- a/packages/rrweb/typings/record/index.d.ts +++ b/packages/rrweb/typings/record/index.d.ts @@ -4,6 +4,6 @@ declare namespace record { var addCustomEvent: (tag: string, payload: T) => void; var freezePage: () => void; var takeFullSnapshot: (isCheckout?: boolean | undefined) => void; - var mirror: import("../types").Mirror; + var mirror: import("rrweb-snapshot").Mirror; } export default record; diff --git a/packages/rrweb/typings/record/observers/canvas/2d.d.ts b/packages/rrweb/typings/record/observers/canvas/2d.d.ts index cb7b7f2e94..fee00970e9 100644 --- a/packages/rrweb/typings/record/observers/canvas/2d.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/2d.d.ts @@ -1,2 +1,3 @@ -import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler, Mirror } from '../../../types'; +import { Mirror } from 'rrweb-snapshot'; +import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; export default function initCanvas2DMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts index 2a3eaf3461..94a913e002 100644 --- a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts @@ -1,4 +1,5 @@ -import { blockClass, canvasMutationCallback, IWindow, Mirror } from '../../../types'; +import { Mirror } from 'rrweb-snapshot'; +import { blockClass, canvasMutationCallback, IWindow } from '../../../types'; export declare type RafStamps = { latestId: number; invokeId: number | null; diff --git a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts b/packages/rrweb/typings/record/observers/canvas/webgl.d.ts index 0f446770a5..7f866cd8e0 100644 --- a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/webgl.d.ts @@ -1,2 +1,3 @@ -import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler, Mirror } from '../../../types'; +import { Mirror } from 'rrweb-snapshot'; +import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; export default function initCanvasWebGLMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/shadow-dom-manager.d.ts b/packages/rrweb/typings/record/shadow-dom-manager.d.ts index e544e2f6b5..7f973a93d8 100644 --- a/packages/rrweb/typings/record/shadow-dom-manager.d.ts +++ b/packages/rrweb/typings/record/shadow-dom-manager.d.ts @@ -1,4 +1,5 @@ -import { mutationCallBack, Mirror, scrollCallback, MutationBufferParam, SamplingStrategy } from '../types'; +import { mutationCallBack, scrollCallback, MutationBufferParam, SamplingStrategy } from '../types'; +import { Mirror } from 'rrweb-snapshot'; declare type BypassOptions = Omit & { sampling: SamplingStrategy; }; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index 1f8a5a1455..3f0cc85d62 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -1,6 +1,7 @@ +import { Mirror } from 'rrweb-snapshot'; import { Timer } from './timer'; import { createPlayerService, createSpeedService } from './machine'; -import { eventWithTime, playerConfig, playerMetaData, Handler, Mirror } from '../types'; +import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types'; import './styles/style.css'; export declare class Replayer { wrapper: HTMLDivElement; diff --git a/packages/rrweb/typings/replay/virtual-styles.d.ts b/packages/rrweb/typings/replay/virtual-styles.d.ts index 11ebf52d1b..ad37eed73c 100644 --- a/packages/rrweb/typings/replay/virtual-styles.d.ts +++ b/packages/rrweb/typings/replay/virtual-styles.d.ts @@ -1,4 +1,3 @@ -import { INode } from 'rrweb-snapshot'; export declare enum StyleRuleType { Insert = 0, Remove = 1, @@ -32,7 +31,7 @@ declare type RemovePropertyRule = { property: string; }; export declare type VirtualStyleRules = Array; -export declare type VirtualStyleRulesMap = Map; +export declare type VirtualStyleRulesMap = Map; export declare function getNestedRule(rules: CSSRuleList, position: number[]): CSSGroupingRule; export declare function getPositionsAndIndex(nestedIndex: number[]): { positions: number[]; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index ae3aa94ca3..913853afcf 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -1,4 +1,4 @@ -import { serializedNodeWithId, idNodeMap, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot'; +import { serializedNodeWithId, Mirror, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot'; import { PackFn, UnpackFn } from './packer/base'; import { IframeManager } from './record/iframe-manager'; import { ShadowDomManager } from './record/shadow-dom-manager'; @@ -401,11 +401,13 @@ export declare type DocumentDimension = { relativeScale: number; absoluteScale: number; }; -export declare type Mirror = { - map: idNodeMap; - getId: (n: INode) => number; +export declare type DeprecatedMirror = { + map: { + [key: number]: INode; + }; + getId: (n: Node) => number; getNode: (id: number) => INode | null; - removeNodeFromMap: (n: INode) => void; + removeNodeFromMap: (n: Node) => void; has: (id: number) => boolean; reset: () => void; }; diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index bdd5d741d2..5e5a26478b 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -1,8 +1,7 @@ -import { Mirror, throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, removedNodeMutation, textMutation, attributeMutation, mutationData, scrollData, inputData, DocumentDimension, IWindow } from './types'; -import { INode, serializedNodeWithId } from 'rrweb-snapshot'; +import { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, removedNodeMutation, textMutation, attributeMutation, mutationData, scrollData, inputData, DocumentDimension, IWindow, DeprecatedMirror } from './types'; +import { Mirror } from 'rrweb-snapshot'; export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler; -export declare function createMirror(): Mirror; -export declare let _mirror: Mirror; +export declare let _mirror: DeprecatedMirror; export declare function throttle(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: 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: { @@ -11,8 +10,8 @@ export declare function patch(source: { export declare function getWindowHeight(): number; export declare function getWindowWidth(): number; export declare function isBlocked(node: Node | null, blockClass: blockClass): boolean; -export declare function isIgnored(n: Node | INode): boolean; -export declare function isAncestorRemoved(target: INode, mirror: Mirror): boolean; +export declare function isIgnored(n: Node, mirror: Mirror): boolean; +export declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean; export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent; export declare function polyfill(win?: Window & typeof globalThis): void; export declare type TreeNode = { @@ -54,14 +53,11 @@ declare type ResolveTree = { }; export declare function queueToResolveTrees(queue: addedNodeMutation[]): ResolveTree[]; export declare function iterateResolveTree(tree: ResolveTree, cb: (mutation: addedNodeMutation) => unknown): void; -declare type HTMLIFrameINode = HTMLIFrameElement & { - __sn: serializedNodeWithId; -}; export declare type AppendedIframe = { mutationInQueue: addedNodeMutation; - builtNode: HTMLIFrameINode; + builtNode: HTMLIFrameElement; }; -export declare function isIframeINode(node: INode | ShadowRoot): node is HTMLIFrameINode; +export declare function isSerializedIframe(n: Node, mirror: Mirror): n is HTMLIFrameElement; export declare function getBaseDimension(node: Node, rootIframe: Node): DocumentDimension; export declare function hasShadowRoot(n: T): n is T & { shadowRoot: ShadowRoot; From 93fec1f3e7c6bc0ae869de50a04966e29548a688 Mon Sep 17 00:00:00 2001 From: Rahul Lingala <88366029+rahulrelicx@users.noreply.github.com> Date: Fri, 15 Apr 2022 09:26:25 +0530 Subject: [PATCH 003/201] Fix mutation edge case when blocked class gets unblocked (#867) * Fix mutation edge case when blocked class gets unblocked * Add integration test * Update isSerialized logic --- packages/rrweb/src/record/mutation.ts | 4 +- packages/rrweb/src/utils.ts | 4 + .../__snapshots__/integration.test.ts.snap | 744 ++++++++++++++++++ .../rrweb/test/html/blocked-unblocked.html | 88 +++ packages/rrweb/test/integration.test.ts | 15 + 5 files changed, 854 insertions(+), 1 deletion(-) create mode 100644 packages/rrweb/test/html/blocked-unblocked.html diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 7b8cd716ea..f2e5ed69ba 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -22,6 +22,7 @@ import { isBlocked, isAncestorRemoved, isIgnored, + isSerialized, hasShadowRoot, isSerializedIframe, } from '../utils'; @@ -525,7 +526,8 @@ export default class MutationBuffer { : this.mirror.getId(m.target); if ( isBlocked(m.target, this.blockClass) || - isIgnored(n, this.mirror) + isIgnored(n, this.mirror) || + !isSerialized(n, this.mirror) ) { return; } diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index ebb212162b..c60174133e 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -213,6 +213,10 @@ export function isBlocked(node: Node | null, blockClass: blockClass): boolean { return isBlocked(node.parentNode, blockClass); } +export function isSerialized(n: Node, mirror: Mirror): boolean { + return mirror.getId(n) !== -1; +} + export function isIgnored(n: Node, mirror: Mirror): boolean { // The main part of the slimDOM check happens in // rrweb-snapshot::serializeNodeWithId diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 06290e6068..9a4fa49a15 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -3546,6 +3546,750 @@ exports[`record integration tests can use maskInputOptions to configure which ty ]" `; +exports[`record integration tests mutations should work when blocked class is unblocked 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Uber Application for Codegen Testing\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 7 + } + ], + \\"id\\": 3 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 10 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 12 + } + ], + \\"id\\": 11 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 14 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"h1\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n Verify that block class bugs are fixed\\\\n \\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"first\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"visible\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"VISIBLE\\", + \\"id\\": 26 + } + ], + \\"id\\": 25 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + } + ], + \\"id\\": 23 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 28 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 30 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 31 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 32 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"rr-block\\", + \\"rr_width\\": \\"1904px\\", + \\"rr_height\\": \\"21px\\" + }, + \\"childNodes\\": [], + \\"id\\": 33 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 35 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": { + \\"onclick\\": \\"mutate1()\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"MUTATE\\", + \\"id\\": 40 + } + ], + \\"id\\": 39 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + } + ], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 42 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 43 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 44 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 45 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"second\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"visible2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 50 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"VISIBLE\\", + \\"id\\": 52 + } + ], + \\"id\\": 51 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + } + ], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 54 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 55 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"class\\": \\"rr-block\\", + \\"rr_width\\": \\"1904px\\", + \\"rr_height\\": \\"21px\\" + }, + \\"childNodes\\": [], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 61 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 62 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"br\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 63 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 64 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": { + \\"onclick\\": \\"mutate2()\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"MUTATE\\", + \\"id\\": 66 + } + ], + \\"id\\": 65 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 67 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 68 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 70 + } + ], + \\"id\\": 69 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\", + \\"id\\": 71 + } + ], + \\"id\\": 9 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 33, + \\"attributes\\": { + \\"class\\": \\"notB\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 23, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 72 + } + }, + { + \\"parentId\\": 72, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 73 + } + }, + { + \\"parentId\\": 73, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 74 + } + }, + { + \\"parentId\\": 74, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"I1I2 VISIBLE\\", + \\"id\\": 75 + } + }, + { + \\"parentId\\": 72, + \\"nextId\\": 73, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 76 + } + }, + { + \\"parentId\\": 76, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 77 + } + }, + { + \\"parentId\\": 77, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"I1I1 VISIBLE\\", + \\"id\\": 78 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 65 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 65 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 65 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 65 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 59, + \\"attributes\\": { + \\"class\\": \\"notB\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 49, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 79 + } + }, + { + \\"parentId\\": 79, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 80 + } + }, + { + \\"parentId\\": 80, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 81 + } + }, + { + \\"parentId\\": 81, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"I1I2 VISIBLE\\", + \\"id\\": 82 + } + }, + { + \\"parentId\\": 79, + \\"nextId\\": 80, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 83 + } + }, + { + \\"parentId\\": 83, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"button\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 84 + } + }, + { + \\"parentId\\": 84, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"I1I1 VISIBLE\\", + \\"id\\": 85 + } + } + ] + } + } +]" +`; + exports[`record integration tests should mask texts 1`] = ` "[ { diff --git a/packages/rrweb/test/html/blocked-unblocked.html b/packages/rrweb/test/html/blocked-unblocked.html new file mode 100644 index 0000000000..ff87e546dd --- /dev/null +++ b/packages/rrweb/test/html/blocked-unblocked.html @@ -0,0 +1,88 @@ + + Uber Application for Codegen Testing + + + + + +
+

+ Verify that block class bugs are fixed +

+
+
+
+ +
+


+
+ +
+


+ +
+


+
+
+ +
+


+
+ +
+


+ +
+ diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 6999e3caee..3fa0f0a34d 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -326,6 +326,21 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('mutations should work when blocked class is unblocked', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about: blank'); + await page.setContent(getHtml.call(this, 'blocked-unblocked.html')); + + const elements1 = await page.$x('/html/body/div[1]/button'); + await elements1[0].click(); + + const elements2 = await page.$x('/html/body/div[2]/button'); + await elements2[0].click(); + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('should record DOM node movement 1', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From e238462f305d73bc546d23a53b981fd7ec3b5448 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 18 Apr 2022 07:24:51 +0200 Subject: [PATCH 004/201] Record canvas snapshots N times per second (#859) * Only record canvas when recordCanvas is true * All should be compiled first Makes recompiling+debugging a lot faster * Add support for compiling web workes Replaces @rollup/plugin-typescript for rollup-plugin-typescript2 as the former is incompatible with rollup-plugin-web-worker-loader * Update yarn.lock * Upgrade to typescript 4.5.5 * add support for replay of ImageBitmap in 2d canvas * Snapshot canvases in a web-worker on FPS basis * Fix performance of canvas recording and playback * Wait for all images to be preloaded before checking results * flatten base64 strings, as encoding isn't consistent * Cleanup * Add serializing to 2d canvases as well * Disable blob serialize test We don't have any code for it yet * Upgrade @rollup/plugin-commonjs to 21.0.2 Fixes https://linguinecode.com/post/import-export-appear-at-the-top-level * Move canvas recording options to `sampling` Based on: https://github.com/rrweb-io/rrweb/pull/859#discussion_r846582146 --- docs/recipes/canvas.md | 12 ++ docs/recipes/canvas.zh_CN.md | 12 ++ docs/recipes/optimize-storage.md | 1 + docs/recipes/optimize-storage.zh_CN.md | 1 + guide.md | 78 +++---- guide.zh_CN.md | 2 +- packages/rrweb-player/package.json | 2 +- packages/rrweb/package.json | 10 +- packages/rrweb/rollup.config.js | 24 +-- packages/rrweb/scripts/repl.js | 4 +- packages/rrweb/src/record/index.ts | 1 + .../rrweb/src/record/observers/canvas/2d.ts | 22 +- .../record/observers/canvas/canvas-manager.ts | 121 ++++++++++- .../record/observers/canvas/serialize-args.ts | 41 ++-- .../workers/image-bitmap-data-url-worker.ts | 77 +++++++ .../rrweb/src/record/workers/tsconfig.json | 7 + .../rrweb/src/record/workers/workers.d.ts | 4 + packages/rrweb/src/replay/canvas/2d.ts | 11 +- .../src/replay/canvas/deserialize-args.ts | 92 +++++++++ packages/rrweb/src/replay/canvas/index.ts | 31 ++- packages/rrweb/src/replay/canvas/webgl.ts | 73 +------ packages/rrweb/src/replay/index.ts | 58 ++++-- packages/rrweb/src/types.ts | 41 +++- packages/rrweb/test/e2e/webgl.test.ts | 4 +- .../rrweb/test/html/canvas-webgl-square.html | 151 +++++++------- packages/rrweb/test/record.test.ts | 4 +- .../record/__snapshots__/webgl.test.ts.snap | 159 +++++++++++++++ .../rrweb/test/record/serialize-args.test.ts | 25 +++ packages/rrweb/test/record/webgl.test.ts | 68 +++++- .../test/replay/deserialize-args.test.ts | 100 +++++++-- .../test/replay/preload-all-images.test.ts | 12 +- .../rrweb/test/replay/webgl-mutation.test.ts | 5 +- packages/rrweb/test/utils.ts | 33 +++ .../observers/canvas/canvas-manager.d.ts | 4 +- .../observers/canvas/serialize-args.d.ts | 10 +- .../workers/image-bitmap-data-url-worker.d.ts | 5 + packages/rrweb/typings/replay/canvas/2d.d.ts | 2 +- .../replay/canvas/deserialize-args.d.ts | 7 + .../rrweb/typings/replay/canvas/index.d.ts | 5 +- .../rrweb/typings/replay/canvas/webgl.d.ts | 8 +- packages/rrweb/typings/replay/index.d.ts | 2 + packages/rrweb/typings/types.d.ts | 27 ++- yarn.lock | 193 ++++++++++++++---- 43 files changed, 1199 insertions(+), 350 deletions(-) create mode 100644 packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts create mode 100644 packages/rrweb/src/record/workers/tsconfig.json create mode 100644 packages/rrweb/src/record/workers/workers.d.ts create mode 100644 packages/rrweb/src/replay/canvas/deserialize-args.ts create mode 100644 packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts create mode 100644 packages/rrweb/typings/replay/canvas/deserialize-args.d.ts diff --git a/docs/recipes/canvas.md b/docs/recipes/canvas.md index 8e5c75220c..934461ee12 100644 --- a/docs/recipes/canvas.md +++ b/docs/recipes/canvas.md @@ -11,6 +11,18 @@ rrweb.record({ }); ``` +Alternatively enable image snapshot recording of Canvas at a maximum of 15 frames per second: + +```js +rrweb.record({ + emit(event) {}, + recordCanvas: true, + sampling: { + canvas: 15, + }, +}); +``` + Enable replaying Canvas: ```js diff --git a/docs/recipes/canvas.zh_CN.md b/docs/recipes/canvas.zh_CN.md index c986e0a207..59ba507205 100644 --- a/docs/recipes/canvas.zh_CN.md +++ b/docs/recipes/canvas.zh_CN.md @@ -12,6 +12,18 @@ rrweb.record({ }); ``` +或者启用每秒 15 帧的 Canvas 图像快照记录: + +```js +rrweb.record({ + emit(event) {}, + recordCanvas: true, + sampling: { + canvas: 15, + }, +}); +``` + 回放时对 Canvas 进行回放: ```js diff --git a/docs/recipes/optimize-storage.md b/docs/recipes/optimize-storage.md index f93fe772c4..cd5152593a 100644 --- a/docs/recipes/optimize-storage.md +++ b/docs/recipes/optimize-storage.md @@ -17,6 +17,7 @@ Some common patterns may emit lots of events are: - long list - complex SVG - element with JS controlled animation +- canvas animations ## Sampling diff --git a/docs/recipes/optimize-storage.zh_CN.md b/docs/recipes/optimize-storage.zh_CN.md index f878ac810f..c180d57ad5 100644 --- a/docs/recipes/optimize-storage.zh_CN.md +++ b/docs/recipes/optimize-storage.zh_CN.md @@ -17,6 +17,7 @@ - 长列表 - 复杂的 SVG - 包含 JS 控制动画的元素 +- canvas 动画 ## 抽样策略 diff --git a/guide.md b/guide.md index b95b4c2da3..3b2e058df1 100644 --- a/guide.md +++ b/guide.md @@ -135,30 +135,30 @@ setInterval(save, 10 * 1000); The parameter of `rrweb.record` accepts the following options. -| key | default | description | -| -------------------- | ------------------ | ------------------------------------------------------------ | -| emit | required | the callback function to get emitted events | -| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | -| checkoutEveryNms | - | take a full snapshot after every N ms
refer to the [checkout](#checkout) chapter | -| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | -| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | -| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | -| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | -| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | -| maskAllInputs | false | mask all input content as \* | -| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | -| maskInputFn | - | customize mask input content recording logic | -| maskTextFn | - | customize mask text content recording logic | -| slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | -| inlineStylesheet | true | whether to inline the stylesheet in the events | -| hooks | {} | hooks for events
refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | -| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| recordCanvas | false | whether to record the canvas element | -| inlineImages | false | whether to record the image content | -| collectFonts | false | whether to collect fonts in the website | +| key | default | description | +| -------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| emit | required | the callback function to get emitted events | +| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | +| checkoutEveryNms | - | take a full snapshot after every N ms
refer to the [checkout](#checkout) chapter | +| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | +| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | +| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | +| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | +| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | +| maskAllInputs | false | mask all input content as \* | +| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | +| maskInputFn | - | customize mask input content recording logic | +| maskTextFn | - | customize mask text content recording logic | +| slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | +| inlineStylesheet | true | whether to inline the stylesheet in the events | +| hooks | {} | hooks for events
refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | +| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| recordCanvas | false | Whether to record the canvas element. Available options:
`false`,
`true` | +| inlineImages | false | whether to record the image content | +| collectFonts | false | whether to collect fonts in the website | | userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) | -| plugins | [] | load plugins to provide extended record functions. [What is plugins?](./docs/recipes/plugin.md) | +| plugins | [] | load plugins to provide extended record functions. [What is plugins?](./docs/recipes/plugin.md) | #### Privacy @@ -286,23 +286,23 @@ replayer.pause(5000); The replayer accepts options as its constructor's second parameter, and it has the following options: -| key | default | description | -| ------------------- | ------------- | ------------------------------------------------------------ | -| speed | 1 | replay speed ratio | -| root | document.body | the root element of replayer | -| loadTimeout | 0 | timeout of loading remote style sheet | -| skipInactive | false | whether to skip inactive time | -| showWarning | true | whether to print warning messages during replay | -| showDebug | false | whether to print debug messages during replay | -| blockClass | 'rr-block' | element with the class name will display as a blocked area | -| liveMode | false | whether to enable live mode | -| insertStyleRules | [] | accepts multiple CSS rule string, which will be injected into the replay iframe | -| triggerFocus | true | whether to trigger focus during replay | -| UNSAFE_replayCanvas | false | whether to replay the canvas element. **Enable this will remove the sandbox, which is unsafe.** | +| key | default | description | +| ------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| speed | 1 | replay speed ratio | +| root | document.body | the root element of replayer | +| loadTimeout | 0 | timeout of loading remote style sheet | +| skipInactive | false | whether to skip inactive time | +| showWarning | true | whether to print warning messages during replay | +| showDebug | false | whether to print debug messages during replay | +| blockClass | 'rr-block' | element with the class name will display as a blocked area | +| liveMode | false | whether to enable live mode | +| insertStyleRules | [] | accepts multiple CSS rule string, which will be injected into the replay iframe | +| triggerFocus | true | whether to trigger focus during replay | +| UNSAFE_replayCanvas | false | whether to replay the canvas element. **Enable this will remove the sandbox, which is unsafe.** | | mouseTail | true | whether to show mouse tail during replay. Set to false to disable mouse tail. A complete config can be found in this [type](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L407) | -| unpackFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| logConfig | - | configuration of console output playback, refer to the [console recipe](./docs/recipes/console.md) | -| plugins | [] | load plugins to provide extended replay functions. [What is plugins?](./docs/recipes/plugin.md) | +| unpackFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| logConfig | - | configuration of console output playback, refer to the [console recipe](./docs/recipes/console.md) | +| plugins | [] | load plugins to provide extended replay functions. [What is plugins?](./docs/recipes/plugin.md) | #### Use rrweb-player diff --git a/guide.zh_CN.md b/guide.zh_CN.md index bdb0a93305..a987934518 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -150,7 +150,7 @@ setInterval(save, 10 * 1000); | hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | | packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | | sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| recordCanvas | false | 是否记录 canvas 内容 | +| recordCanvas | false | 是否记录 canvas 内容, 可用选项:false, true | | inlineImages | false | 是否将图片内容记内联录制 | | collectFonts | false | 是否记录页面中的字体文件 | | userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index ec09c21361..55a830e42f 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -2,7 +2,7 @@ "name": "rrweb-player", "version": "0.7.14", "devDependencies": { - "@rollup/plugin-commonjs": "^11.0.0", + "@rollup/plugin-commonjs": "^21.0.2", "@rollup/plugin-node-resolve": "^7.0.0", "@rollup/plugin-typescript": "^4.0.0", "@typescript-eslint/eslint-plugin": "^3.7.0", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 145dd1b6f9..18b32fe1b4 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -41,14 +41,14 @@ }, "homepage": "https://github.com/rrweb-io/rrweb#readme", "devDependencies": { - "@rollup/plugin-node-resolve": "^7.0.0", - "@rollup/plugin-typescript": "^8.3.1", + "@rollup/plugin-node-resolve": "^13.1.3", "@types/chai": "^4.1.6", "@types/inquirer": "0.0.43", "@types/jest": "^27.4.1", "@types/jest-image-snapshot": "^4.3.1", "@types/jsdom": "^16.2.14", "@types/node": "^17.0.21", + "@types/offscreencanvas": "^2019.6.4", "@types/prettier": "^2.3.2", "@types/puppeteer": "^5.4.4", "cross-env": "^5.2.0", @@ -63,10 +63,12 @@ "jsdom-global": "^3.0.2", "prettier": "2.2.1", "puppeteer": "^9.1.1", - "rollup": "^2.45.2", + "rollup": "^2.68.0", "rollup-plugin-postcss": "^3.1.1", - "rollup-plugin-rename-node-modules": "^1.1.0", + "rollup-plugin-rename-node-modules": "^1.3.1", "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", "ts-node": "^10.7.0", "tslib": "^2.3.1", diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index 8d6a3c6633..3c2eff47a1 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -1,8 +1,9 @@ -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import resolve from '@rollup/plugin-node-resolve'; import { terser } from 'rollup-plugin-terser'; import postcss from 'rollup-plugin-postcss'; import renameNodeModules from 'rollup-plugin-rename-node-modules'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; import pkg from './package.json'; function toRecordPath(path) { @@ -45,6 +46,13 @@ function toMinPath(path) { } const baseConfigs = [ + // all in one + { + input: './src/entries/all.ts', + name: 'rrweb', + pathFn: toAllPath, + esm: true, + }, // record only { input: './src/record/index.ts', @@ -75,13 +83,6 @@ const baseConfigs = [ name: 'rrweb', pathFn: (p) => p, }, - // all in one - { - input: './src/entries/all.ts', - name: 'rrweb', - pathFn: toAllPath, - esm: true, - }, // plugins { input: './src/plugins/console/record/index.ts', @@ -110,10 +111,8 @@ let configs = []; for (const c of baseConfigs) { const basePlugins = [ resolve({ browser: true }), - typescript({ - // a trick to avoid @rollup/plugin-typescript error - outDir: 'es/rrweb', - }), + webWorkerLoader(), + typescript(), ]; const plugins = basePlugins.concat( postcss({ @@ -200,6 +199,7 @@ if (process.env.BROWSER_ONLY) { for (const c of browserOnlyBaseConfigs) { const plugins = [ resolve({ browser: true }), + webWorkerLoader(), typescript({ outDir: null, }), diff --git a/packages/rrweb/scripts/repl.js b/packages/rrweb/scripts/repl.js index e4ad49210b..839b8d5ca6 100644 --- a/packages/rrweb/scripts/repl.js +++ b/packages/rrweb/scripts/repl.js @@ -169,7 +169,9 @@ function getCode() { }); await page.evaluate(`${code} const events = ${JSON.stringify(events)}; - const replayer = new rrweb.Replayer(events); + const replayer = new rrweb.Replayer(events, { + UNSAFE_replayCanvas: true + }); replayer.play(); `); } diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 3b6d910ceb..3240e2f42a 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -221,6 +221,7 @@ function record( win: window, blockClass, mirror, + sampling: sampling.canvas, }); const shadowDomManager = new ShadowDomManager({ diff --git a/packages/rrweb/src/record/observers/canvas/2d.ts b/packages/rrweb/src/record/observers/canvas/2d.ts index 6e7049af82..86cf0e396a 100644 --- a/packages/rrweb/src/record/observers/canvas/2d.ts +++ b/packages/rrweb/src/record/observers/canvas/2d.ts @@ -7,6 +7,7 @@ import { listenerHandler, } from '../../../types'; import { hookSetter, isBlocked, patch } from '../../../utils'; +import { serializeArgs } from './serialize-args'; export default function initCanvas2DMutationObserver( cb: canvasManagerMutationCallback, @@ -36,27 +37,10 @@ export default function initCanvas2DMutationObserver( ...args: Array ) { if (!isBlocked(this.canvas, blockClass)) { - // Using setTimeout as getImageData + JSON.stringify can be heavy + // Using setTimeout as toDataURL can be heavy // and we'd rather not block the main thread setTimeout(() => { - const recordArgs = [...args]; - if (prop === 'drawImage') { - if ( - recordArgs[0] && - recordArgs[0] instanceof HTMLCanvasElement - ) { - const canvas = recordArgs[0]; - const ctx = canvas.getContext('2d'); - let imgd = ctx?.getImageData( - 0, - 0, - canvas.width, - canvas.height, - ); - let pix = imgd?.data; - recordArgs[0] = JSON.stringify(pix); - } - } + const recordArgs = serializeArgs([...args], win, this); cb(this.canvas, { type: CanvasContext['2D'], property: prop, diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index 007419b37d..79859dbbc8 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -1,16 +1,20 @@ -import { Mirror } from 'rrweb-snapshot'; +import { ICanvas, Mirror } from 'rrweb-snapshot'; import { blockClass, + CanvasContext, canvasManagerMutationCallback, canvasMutationCallback, canvasMutationCommand, canvasMutationWithType, IWindow, listenerHandler, + CanvasArg, } from '../../../types'; import initCanvas2DMutationObserver from './2d'; import initCanvasContextObserver from './canvas'; import initCanvasWebGLMutationObserver from './webgl'; +import ImageBitmapDataURLWorker from 'web-worker:../../workers/image-bitmap-data-url-worker.ts'; +import { ImageBitmapDataURLRequestWorker } from '../../workers/image-bitmap-data-url-worker'; export type RafStamps = { latestId: number; invokeId: number | null }; @@ -51,17 +55,21 @@ export class CanvasManager { } constructor(options: { - recordCanvas: boolean | number; + recordCanvas: boolean; mutationCb: canvasMutationCallback; win: IWindow; blockClass: blockClass; mirror: Mirror; + sampling?: 'all' | number; }) { + const { sampling = 'all', win, blockClass, recordCanvas } = options; this.mutationCb = options.mutationCb; this.mirror = options.mirror; - if (options.recordCanvas === true) - this.initCanvasMutationObserver(options.win, options.blockClass); + if (recordCanvas && sampling === 'all') + this.initCanvasMutationObserver(win, blockClass); + if (recordCanvas && typeof sampling === 'number') + this.initCanvasFPSObserver(sampling, win, blockClass); } private processMutation: canvasManagerMutationCallback = function ( @@ -81,6 +89,111 @@ export class CanvasManager { this.pendingCanvasMutations.get(target)!.push(mutation); }; + private initCanvasFPSObserver( + fps: number, + win: IWindow, + blockClass: blockClass, + ) { + const canvasContextReset = initCanvasContextObserver(win, blockClass); + const snapshotInProgressMap: Map = new Map(); + const worker = new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker; + worker.onmessage = (e) => { + const { id } = e.data; + snapshotInProgressMap.set(id, false); + + if (!('base64' in e.data)) return; + + const { base64, type, width, height } = e.data; + this.mutationCb({ + id, + type: CanvasContext['2D'], + commands: [ + { + property: 'clearRect', // wipe canvas + args: [0, 0, width, height], + }, + { + property: 'drawImage', // draws (semi-transparent) image + args: [ + { + rr_type: 'ImageBitmap', + args: [ + { + rr_type: 'Blob', + data: [{ rr_type: 'ArrayBuffer', base64 }], + type, + }, + ], + } as CanvasArg, + 0, + 0, + ], + }, + ], + }); + }; + + const timeBetweenSnapshots = 1000 / fps; + let lastSnapshotTime = 0; + let rafId: number; + + const takeCanvasSnapshots = (timestamp: DOMHighResTimeStamp) => { + if ( + lastSnapshotTime && + timestamp - lastSnapshotTime < timeBetweenSnapshots + ) { + rafId = requestAnimationFrame(takeCanvasSnapshots); + return; + } + lastSnapshotTime = timestamp; + + win.document + .querySelectorAll(`canvas:not(.${blockClass} *)`) + .forEach(async (canvas: HTMLCanvasElement) => { + const id = this.mirror.getId(canvas); + if (snapshotInProgressMap.get(id)) return; + snapshotInProgressMap.set(id, true); + if (['webgl', 'webgl2'].includes((canvas as ICanvas).__context)) { + // if the canvas hasn't been modified recently, + // its contents won't be in memory and `createImageBitmap` + // will return a transparent imageBitmap + + const context = canvas.getContext((canvas as ICanvas).__context) as + | WebGLRenderingContext + | WebGL2RenderingContext + | null; + if ( + context?.getContextAttributes()?.preserveDrawingBuffer === false + ) { + // Hack to load canvas back into memory so `createImageBitmap` can grab it's contents. + // Context: https://twitter.com/Juice10/status/1499775271758704643 + // This hack might change the background color of the canvas in the unlikely event that + // the canvas background was changed but clear was not called directly afterwards. + context?.clear(context.COLOR_BUFFER_BIT); + } + } + const bitmap = await createImageBitmap(canvas); + worker.postMessage( + { + id, + bitmap, + width: canvas.width, + height: canvas.height, + }, + [bitmap], + ); + }); + rafId = requestAnimationFrame(takeCanvasSnapshots); + }; + + rafId = requestAnimationFrame(takeCanvasSnapshots); + + this.resetObservers = () => { + canvasContextReset(); + cancelAnimationFrame(rafId); + }; + } + private initCanvasMutationObserver( win: IWindow, blockClass: blockClass, diff --git a/packages/rrweb/src/record/observers/canvas/serialize-args.ts b/packages/rrweb/src/record/observers/canvas/serialize-args.ts index e245ee4123..98095b3a61 100644 --- a/packages/rrweb/src/record/observers/canvas/serialize-args.ts +++ b/packages/rrweb/src/record/observers/canvas/serialize-args.ts @@ -1,20 +1,14 @@ import { encode } from 'base64-arraybuffer'; -import { IWindow, SerializedWebGlArg } from '../../../types'; +import { IWindow, CanvasArg } from '../../../types'; // TODO: unify with `replay/webgl.ts` -type GLVarMap = Map; -const webGLVarMap: Map< - WebGLRenderingContext | WebGL2RenderingContext, - GLVarMap -> = new Map(); -export function variableListFor( - ctx: WebGLRenderingContext | WebGL2RenderingContext, - ctor: string, -) { - let contextMap = webGLVarMap.get(ctx); +type CanvasVarMap = Map; +const canvasVarMap: Map = new Map(); +export function variableListFor(ctx: RenderingContext, ctor: string) { + let contextMap = canvasVarMap.get(ctx); if (!contextMap) { contextMap = new Map(); - webGLVarMap.set(ctx, contextMap); + canvasVarMap.set(ctx, contextMap); } if (!contextMap.has(ctor)) { contextMap.set(ctor, []); @@ -25,7 +19,7 @@ export function variableListFor( export const saveWebGLVar = ( value: any, win: IWindow, - ctx: WebGL2RenderingContext | WebGLRenderingContext, + ctx: RenderingContext, ): number | void => { if ( !value || @@ -48,8 +42,8 @@ export const saveWebGLVar = ( export function serializeArg( value: any, win: IWindow, - ctx: WebGL2RenderingContext | WebGLRenderingContext, -): SerializedWebGlArg { + ctx: RenderingContext, +): CanvasArg { if (value instanceof Array) { return value.map((arg) => serializeArg(arg, win, ctx)); } else if (value === null) { @@ -100,12 +94,27 @@ export function serializeArg( rr_type: name, src, }; + } else if (value instanceof HTMLCanvasElement) { + const name = 'HTMLImageElement'; + // TODO: move `toDataURL` to web worker if possible + const src = value.toDataURL(); // heavy on large canvas + return { + rr_type: name, + src, + }; } else if (value instanceof ImageData) { const name = value.constructor.name; return { rr_type: name, args: [serializeArg(value.data, win, ctx), value.width, value.height], }; + // } else if (value instanceof Blob) { + // const name = value.constructor.name; + // return { + // rr_type: name, + // data: [serializeArg(await value.arrayBuffer(), win, ctx)], + // type: value.type, + // }; } else if (isInstanceOfWebGLObject(value, win) || typeof value === 'object') { const name = value.constructor.name; const index = saveWebGLVar(value, win, ctx) as number; @@ -122,7 +131,7 @@ export function serializeArg( export const serializeArgs = ( args: Array, win: IWindow, - ctx: WebGLRenderingContext | WebGL2RenderingContext, + ctx: RenderingContext, ) => { return [...args].map((arg) => serializeArg(arg, win, ctx)); }; diff --git a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts new file mode 100644 index 0000000000..28cc5c8b72 --- /dev/null +++ b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts @@ -0,0 +1,77 @@ +import { encode } from 'base64-arraybuffer'; +import { + ImageBitmapDataURLWorkerParams, + ImageBitmapDataURLWorkerResponse, +} from '../../types'; + +const lastBlobMap: Map = new Map(); +const transparentBlobMap: Map = new Map(); + +export interface ImageBitmapDataURLRequestWorker { + postMessage: ( + message: ImageBitmapDataURLWorkerParams, + transfer?: [ImageBitmap], + ) => void; + onmessage: (message: MessageEvent) => void; +} + +interface ImageBitmapDataURLResponseWorker { + onmessage: + | null + | ((message: MessageEvent) => void); + postMessage(e: ImageBitmapDataURLWorkerResponse): void; +} + +async function getTransparentBlobFor( + width: number, + height: number, +): Promise { + const id = `${width}-${height}`; + if (transparentBlobMap.has(id)) return transparentBlobMap.get(id)!; + const offscreen = new OffscreenCanvas(width, height); + offscreen.getContext('2d'); // creates rendering context for `converToBlob` + const blob = await offscreen.convertToBlob(); // takes a while + const arrayBuffer = await blob.arrayBuffer(); + const base64 = encode(arrayBuffer); // cpu intensive + transparentBlobMap.set(id, base64); + return base64; +} + +// `as any` because: https://github.com/Microsoft/TypeScript/issues/20595 +const worker: ImageBitmapDataURLResponseWorker = self; + +worker.onmessage = async function (e) { + if (!('OffscreenCanvas' in globalThis)) + return worker.postMessage({ id: e.data.id }); + + const { id, bitmap, width, height } = e.data; + + const transparentBase64 = getTransparentBlobFor(width, height); + + const offscreen = new OffscreenCanvas(width, height); + const ctx = offscreen.getContext('2d')!; + + ctx.drawImage(bitmap, 0, 0); + bitmap.close(); + const blob = await offscreen.convertToBlob(); // takes a while + const type = blob.type; + const arrayBuffer = await blob.arrayBuffer(); + const base64 = encode(arrayBuffer); // cpu intensive + + // on first try we should check if canvas is transparent, + // no need to save it's contents in that case + if (!lastBlobMap.has(id) && (await transparentBase64) === base64) { + lastBlobMap.set(id, base64); + return worker.postMessage({ id }); + } + + if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); // unchanged + worker.postMessage({ + id, + type, + base64, + width, + height, + }); + lastBlobMap.set(id, base64); +}; diff --git a/packages/rrweb/src/record/workers/tsconfig.json b/packages/rrweb/src/record/workers/tsconfig.json new file mode 100644 index 0000000000..cf0e05cb8e --- /dev/null +++ b/packages/rrweb/src/record/workers/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "lib": ["webworker"] + }, + "exclude": ["workers.d.ts"] +} diff --git a/packages/rrweb/src/record/workers/workers.d.ts b/packages/rrweb/src/record/workers/workers.d.ts new file mode 100644 index 0000000000..ead3d9e1f4 --- /dev/null +++ b/packages/rrweb/src/record/workers/workers.d.ts @@ -0,0 +1,4 @@ +declare module 'web-worker:*' { + const WorkerFactory: new () => Worker; + export default WorkerFactory; +} diff --git a/packages/rrweb/src/replay/canvas/2d.ts b/packages/rrweb/src/replay/canvas/2d.ts index feeb94d8c2..f535f24bac 100644 --- a/packages/rrweb/src/replay/canvas/2d.ts +++ b/packages/rrweb/src/replay/canvas/2d.ts @@ -1,7 +1,8 @@ import { Replayer } from '../'; import { canvasMutationCommand } from '../../types'; +import { deserializeArg } from './deserialize-args'; -export default function canvasMutation({ +export default async function canvasMutation({ event, mutation, target, @@ -13,7 +14,7 @@ export default function canvasMutation({ target: HTMLCanvasElement; imageMap: Replayer['imageMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void { +}): Promise { try { const ctx = target.getContext('2d')!; @@ -37,10 +38,12 @@ export default function canvasMutation({ typeof mutation.args[0] === 'string' ) { const image = imageMap.get(event); - mutation.args[0] = image; original.apply(ctx, mutation.args); } else { - original.apply(ctx, mutation.args); + const args = await Promise.all( + mutation.args.map(deserializeArg(imageMap, ctx)), + ); + original.apply(ctx, args); } } catch (error) { errorHandler(mutation, error); diff --git a/packages/rrweb/src/replay/canvas/deserialize-args.ts b/packages/rrweb/src/replay/canvas/deserialize-args.ts new file mode 100644 index 0000000000..d5adcbbf7d --- /dev/null +++ b/packages/rrweb/src/replay/canvas/deserialize-args.ts @@ -0,0 +1,92 @@ +import { decode } from 'base64-arraybuffer'; +import type { Replayer } from '../'; +import { CanvasArg, SerializedCanvasArg } from '../../types'; + +// TODO: add ability to wipe this list +type GLVarMap = Map; +const webGLVarMap: Map< + CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext, + GLVarMap +> = new Map(); +export function variableListFor( + ctx: + | CanvasRenderingContext2D + | WebGLRenderingContext + | WebGL2RenderingContext, + ctor: string, +) { + let contextMap = webGLVarMap.get(ctx); + if (!contextMap) { + contextMap = new Map(); + webGLVarMap.set(ctx, contextMap); + } + if (!contextMap.has(ctor)) { + contextMap.set(ctor, []); + } + return contextMap.get(ctor) as any[]; +} + +export function isSerializedArg(arg: unknown): arg is SerializedCanvasArg { + return Boolean(arg && typeof arg === 'object' && 'rr_type' in arg); +} + +export function deserializeArg( + imageMap: Replayer['imageMap'], + ctx: + | CanvasRenderingContext2D + | WebGLRenderingContext + | WebGL2RenderingContext + | null, + preload?: { + isUnchanged: boolean; + }, +): (arg: CanvasArg) => Promise { + return async (arg: CanvasArg): Promise => { + if (arg && typeof arg === 'object' && 'rr_type' in arg) { + if (preload) preload.isUnchanged = false; + if (arg.rr_type === 'ImageBitmap' && 'args' in arg) { + const args = await deserializeArg(imageMap, ctx, preload)(arg.args); + return await createImageBitmap.apply(null, args); + } else if ('index' in arg) { + if (preload || ctx === null) return arg; // we are preloading, ctx is unknown + const { rr_type: name, index } = arg; + return variableListFor(ctx, name)[index]; + } else if ('args' in arg) { + const { rr_type: name, args } = arg; + const ctor = window[name as keyof Window]; + + return new ctor( + ...(await Promise.all( + args.map(deserializeArg(imageMap, ctx, preload)), + )), + ); + } else if ('base64' in arg) { + return decode(arg.base64); + } else if ('src' in arg) { + const image = imageMap.get(arg.src); + if (image) { + return image; + } else { + const image = new Image(); + image.src = arg.src; + imageMap.set(arg.src, image); + return image; + } + } else if ('data' in arg && arg.rr_type === 'Blob') { + const blobContents = await Promise.all( + arg.data.map(deserializeArg(imageMap, ctx, preload)), + ); + const blob = new Blob(blobContents, { + type: arg.type, + }); + return blob; + } + } else if (Array.isArray(arg)) { + const result = await Promise.all( + arg.map(deserializeArg(imageMap, ctx, preload)), + ); + return result; + } + return arg; + }; +} diff --git a/packages/rrweb/src/replay/canvas/index.ts b/packages/rrweb/src/replay/canvas/index.ts index 73411a2b10..8924b9ec31 100644 --- a/packages/rrweb/src/replay/canvas/index.ts +++ b/packages/rrweb/src/replay/canvas/index.ts @@ -3,48 +3,59 @@ import { CanvasContext, canvasMutationCommand, canvasMutationData, + canvasMutationParam, } from '../../types'; import webglMutation from './webgl'; import canvas2DMutation from './2d'; -export default function canvasMutation({ +export default async function canvasMutation({ event, mutation, target, imageMap, + canvasEventMap, errorHandler, }: { event: Parameters[0]; mutation: canvasMutationData; target: HTMLCanvasElement; imageMap: Replayer['imageMap']; + canvasEventMap: Replayer['canvasEventMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void { +}): Promise { try { - const mutations: canvasMutationCommand[] = - 'commands' in mutation ? mutation.commands : [mutation]; + let precomputedMutation: canvasMutationParam = + canvasEventMap.get(event) || mutation; + + const commands: canvasMutationCommand[] = + 'commands' in precomputedMutation + ? precomputedMutation.commands + : [precomputedMutation]; if ([CanvasContext.WebGL, CanvasContext.WebGL2].includes(mutation.type)) { - return mutations.forEach((command) => { - webglMutation({ + for (let i = 0; i < commands.length; i++) { + const command = commands[i]; + await webglMutation({ mutation: command, type: mutation.type, target, imageMap, errorHandler, }); - }); + } + return; } // default is '2d' for backwards compatibility (rrweb below 1.1.x) - return mutations.forEach((command) => { - canvas2DMutation({ + for (let i = 0; i < commands.length; i++) { + const command = commands[i]; + await canvas2DMutation({ event, mutation: command, target, imageMap, errorHandler, }); - }); + } } catch (error) { errorHandler(mutation, error); } diff --git a/packages/rrweb/src/replay/canvas/webgl.ts b/packages/rrweb/src/replay/canvas/webgl.ts index 58d323dc3d..67bffb6ff8 100644 --- a/packages/rrweb/src/replay/canvas/webgl.ts +++ b/packages/rrweb/src/replay/canvas/webgl.ts @@ -1,31 +1,6 @@ -import { decode } from 'base64-arraybuffer'; -import { Replayer } from '../'; -import { - CanvasContext, - canvasMutationCommand, - SerializedWebGlArg, -} from '../../types'; - -// TODO: add ability to wipe this list -type GLVarMap = Map; -const webGLVarMap: Map< - WebGLRenderingContext | WebGL2RenderingContext, - GLVarMap -> = new Map(); -export function variableListFor( - ctx: WebGLRenderingContext | WebGL2RenderingContext, - ctor: string, -) { - let contextMap = webGLVarMap.get(ctx); - if (!contextMap) { - contextMap = new Map(); - webGLVarMap.set(ctx, contextMap); - } - if (!contextMap.has(ctor)) { - contextMap.set(ctor, []); - } - return contextMap.get(ctor) as any[]; -} +import type { Replayer } from '../'; +import { CanvasContext, canvasMutationCommand } from '../../types'; +import { deserializeArg, variableListFor } from './deserialize-args'; function getContext( target: HTMLCanvasElement, @@ -72,41 +47,7 @@ function saveToWebGLVarMap( if (!variables.includes(result)) variables.push(result); } -export function deserializeArg( - imageMap: Replayer['imageMap'], - ctx: WebGLRenderingContext | WebGL2RenderingContext, -): (arg: SerializedWebGlArg) => any { - return (arg: SerializedWebGlArg): any => { - if (arg && typeof arg === 'object' && 'rr_type' in arg) { - if ('index' in arg) { - const { rr_type: name, index } = arg; - return variableListFor(ctx, name)[index]; - } else if ('args' in arg) { - const { rr_type: name, args } = arg; - const ctor = window[name as keyof Window]; - - return new ctor(...args.map(deserializeArg(imageMap, ctx))); - } else if ('base64' in arg) { - return decode(arg.base64); - } else if ('src' in arg) { - const image = imageMap.get(arg.src); - if (image) { - return image; - } else { - const image = new Image(); - image.src = arg.src; - imageMap.set(arg.src, image); - return image; - } - } - } else if (Array.isArray(arg)) { - return arg.map(deserializeArg(imageMap, ctx)); - } - return arg; - }; -} - -export default function webglMutation({ +export default async function webglMutation({ mutation, target, type, @@ -118,7 +59,7 @@ export default function webglMutation({ type: CanvasContext; imageMap: Replayer['imageMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void { +}): Promise { try { const ctx = getContext(target, type); if (!ctx) return; @@ -137,7 +78,9 @@ export default function webglMutation({ mutation.property as Exclude ] as Function; - const args = mutation.args.map(deserializeArg(imageMap, ctx)); + const args = await Promise.all( + mutation.args.map(deserializeArg(imageMap, ctx)), + ); const result = original.apply(ctx, args); saveToWebGLVarMap(ctx, result); diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index d7027a14bf..e3724d1b14 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -40,6 +40,7 @@ import { mouseMovePos, IWindow, canvasMutationCommand, + canvasMutationParam, textMutation, } from '../types'; import { @@ -64,6 +65,7 @@ import { getPositionsAndIndex, } from './virtual-styles'; import canvasMutation from './canvas'; +import { deserializeArg } from './canvas/deserialize-args'; const SKIP_TIME_THRESHOLD = 10 * 1000; const SKIP_TIME_INTERVAL = 5 * 1000; @@ -123,6 +125,7 @@ export class Replayer { private cache: BuildCache = createCache(); private imageMap: Map = new Map(); + private canvasEventMap: Map = new Map(); private mirror: Mirror = createMirror(); @@ -854,24 +857,30 @@ export class Replayer { /** * pause when there are some canvas drawImage args need to be loaded */ - private preloadAllImages() { + private async preloadAllImages(): Promise { let beforeLoadState = this.service.state; const stateHandler = () => { beforeLoadState = this.service.state; }; this.emitter.on(ReplayerEvents.Start, stateHandler); this.emitter.on(ReplayerEvents.Pause, stateHandler); + const promises: Promise[] = []; for (const event of this.service.state.context.events) { if ( event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.CanvasMutation - ) - if ('commands' in event.data) { - event.data.commands.forEach((c) => this.preloadImages(c, event)); - } else { - this.preloadImages(event.data, event); - } + ) { + promises.push( + this.deserializeAndPreloadCanvasEvents(event.data, event), + ); + const commands = + 'commands' in event.data ? event.data.commands : [event.data]; + commands.forEach((c) => { + this.preloadImages(c, event); + }); + } } + return Promise.all(promises); } private preloadImages(data: canvasMutationCommand, event: eventWithTime) { @@ -886,12 +895,34 @@ export class Replayer { let d = imgd?.data; d = JSON.parse(data.args[0]); ctx?.putImageData(imgd!, 0, 0); - } else if (this.hasImageArg(data.args)) { - this.getImageArgs(data.args).forEach((url) => { - const image = new Image(); - image.src = url; // this preloads the image - this.imageMap.set(url, image); - }); + } + } + private async deserializeAndPreloadCanvasEvents( + data: canvasMutationData, + event: eventWithTime, + ) { + if (!this.canvasEventMap.has(event)) { + const status = { + isUnchanged: true, + }; + if ('commands' in data) { + const commands = await Promise.all( + data.commands.map(async (c) => { + const args = await Promise.all( + c.args.map(deserializeArg(this.imageMap, null, status)), + ); + return { ...c, args }; + }), + ); + if (status.isUnchanged === false) + this.canvasEventMap.set(event, { ...data, commands }); + } else { + const args = await Promise.all( + data.args.map(deserializeArg(this.imageMap, null, status)), + ); + if (status.isUnchanged === false) + this.canvasEventMap.set(event, { ...data, args }); + } } } @@ -1282,6 +1313,7 @@ export class Replayer { mutation: d, target: target as HTMLCanvasElement, imageMap: this.imageMap, + canvasEventMap: this.canvasEventMap, errorHandler: this.warnCanvasMutationFailed.bind(this), }); diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index acf02173aa..629df4baa5 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -201,6 +201,12 @@ export type SamplingStrategy = Partial<{ * 'last' will only record the last input value while input a sequence of chars */ input: 'all' | 'last'; + /** + * 'all' will record every single canvas call + * number between 1 and 60, will record an image snapshots in a web-worker a (maximum) number of times per second. + * Number only supported where [`OffscreenCanvas`](http://mdn.io/offscreencanvas) is supported. + */ + canvas: 'all' | number; }>; export type RecordPlugin = { @@ -416,28 +422,36 @@ export enum CanvasContext { WebGL2, } -export type SerializedWebGlArg = +export type SerializedCanvasArg = | { rr_type: 'ArrayBuffer'; base64: string; // base64 } + | { + rr_type: 'Blob'; + data: Array; + type?: string; + } | { rr_type: string; src: string; // url of image } | { rr_type: string; - args: SerializedWebGlArg[]; + args: Array; } | { rr_type: string; index: number; - } + }; + +export type CanvasArg = + | SerializedCanvasArg | string | number | boolean | null - | SerializedWebGlArg[]; + | CanvasArg[]; type mouseInteractionParam = { type: MouseInteractions; @@ -516,6 +530,25 @@ export type canvasManagerMutationCallback = ( p: canvasMutationWithType, ) => void; +export type ImageBitmapDataURLWorkerParams = { + id: number; + bitmap: ImageBitmap; + width: number; + height: number; +}; + +export type ImageBitmapDataURLWorkerResponse = + | { + id: number; + } + | { + id: number; + type: string; + base64: string; + width: number; + height: number; + }; + export type fontParam = { family: string; fontSource: string; diff --git a/packages/rrweb/test/e2e/webgl.test.ts b/packages/rrweb/test/e2e/webgl.test.ts index 2be0d33f0c..38c4abc880 100644 --- a/packages/rrweb/test/e2e/webgl.test.ts +++ b/packages/rrweb/test/e2e/webgl.test.ts @@ -130,7 +130,7 @@ describe('e2e webgl', () => { }); replayer.play(500); `); - await page.waitForTimeout(50); + await waitForRAF(page); const element = await page.$('iframe'); const frameImage = await element!.screenshot(); @@ -165,7 +165,7 @@ describe('e2e webgl', () => { // wait for iframe to get added and `preloadAllImages` to ge called await page.waitForSelector('iframe'); await page.evaluate(`replayer.play(500);`); - await page.waitForTimeout(50); + await waitForRAF(page); const element = await page.$('iframe'); const frameImage = await element!.screenshot(); diff --git a/packages/rrweb/test/html/canvas-webgl-square.html b/packages/rrweb/test/html/canvas-webgl-square.html index cbdc7ec62e..0388cd9459 100644 --- a/packages/rrweb/test/html/canvas-webgl-square.html +++ b/packages/rrweb/test/html/canvas-webgl-square.html @@ -32,79 +32,84 @@ } diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 5b9e82b851..8043bea6af 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -11,7 +11,7 @@ import { IncrementalSource, styleSheetRuleData, } from '../src/types'; -import { assertSnapshot, launchPuppeteer } from './utils'; +import { assertSnapshot, launchPuppeteer, waitForRAF } from './utils'; interface ISuite { code: string; @@ -368,7 +368,7 @@ describe('record iframes', function (this: ISuite) { emit: ((window as unknown) as IWindow).emit, }); }); - await ctx.page.waitForTimeout(10); + await waitForRAF(ctx.page); // console.log(JSON.stringify(ctx.events)); expect(ctx.events.length).toEqual(3); diff --git a/packages/rrweb/test/record/__snapshots__/webgl.test.ts.snap b/packages/rrweb/test/record/__snapshots__/webgl.test.ts.snap index 03a208dcd5..c7aeb17e1c 100644 --- a/packages/rrweb/test/record/__snapshots__/webgl.test.ts.snap +++ b/packages/rrweb/test/record/__snapshots__/webgl.test.ts.snap @@ -1,5 +1,164 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`record webgl recordCanvas FPS should record snapshots 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 6 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": { + \\"id\\": \\"canvas\\" + }, + \\"childNodes\\": [], + \\"id\\": 7 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 8 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 9, + \\"id\\": 7, + \\"type\\": 0, + \\"commands\\": [ + { + \\"property\\": \\"clearRect\\", + \\"args\\": [ + 0, + 0, + 300, + 150 + ] + }, + { + \\"property\\": \\"drawImage\\", + \\"args\\": [ + { + \\"rr_type\\": \\"ImageBitmap\\", + \\"args\\": [ + { + \\"rr_type\\": \\"Blob\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"base64-0\\" + } + ], + \\"type\\": \\"image/png\\" + } + ] + }, + 0, + 0 + ] + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 9, + \\"id\\": 7, + \\"type\\": 0, + \\"commands\\": [ + { + \\"property\\": \\"clearRect\\", + \\"args\\": [ + 0, + 0, + 300, + 150 + ] + }, + { + \\"property\\": \\"drawImage\\", + \\"args\\": [ + { + \\"rr_type\\": \\"ImageBitmap\\", + \\"args\\": [ + { + \\"rr_type\\": \\"Blob\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"base64-1\\" + } + ], + \\"type\\": \\"image/png\\" + } + ] + }, + 0, + 0 + ] + } + ] + } + } +]" +`; + exports[`record webgl should batch events by RAF 1`] = ` "[ { diff --git a/packages/rrweb/test/record/serialize-args.test.ts b/packages/rrweb/test/record/serialize-args.test.ts index 8ba72e6cd0..8474c03938 100644 --- a/packages/rrweb/test/record/serialize-args.test.ts +++ b/packages/rrweb/test/record/serialize-args.test.ts @@ -149,6 +149,16 @@ describe('serializeArg', () => { }); }); + it('should support HTMLCanvasElements saved to image', async () => { + const canvas = document.createElement('canvas'); + // polyfill canvas.toDataURL as it doesn't exist in jsdom + canvas.toDataURL = () => 'data:image/png;base64,...'; + expect(serializeArg(canvas, window, context)).toMatchObject({ + rr_type: 'HTMLImageElement', + src: 'data:image/png;base64,...', + }); + }); + it('should serialize ImageData', async () => { const arr = new Uint8ClampedArray(40000); @@ -176,4 +186,19 @@ describe('serializeArg', () => { ], }); }); + + // we do not yet support async serializing which is needed to call Blob.arrayBuffer() + it.skip('should serialize a blob', async () => { + const arrayBuffer = new Uint8Array([1, 2, 0, 4]).buffer; + const blob = new Blob([arrayBuffer], { type: 'image/png' }); + const expected = { + rr_type: 'ArrayBuffer', + base64: 'AQIABA==', + }; + + expect(await serializeArg(blob, window, context)).toStrictEqual({ + rr_type: 'Blob', + args: [expected, { type: 'image/png' }], + }); + }); }); diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index 17442669d4..9280e7fb60 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -11,7 +11,12 @@ import { IncrementalSource, CanvasContext, } from '../../src/types'; -import { assertSnapshot, launchPuppeteer, waitForRAF } from '../utils'; +import { + assertSnapshot, + launchPuppeteer, + stripBase64, + waitForRAF, +} from '../utils'; import { ICanvas } from 'rrweb-snapshot'; interface ISuite { @@ -31,7 +36,11 @@ interface IWindow extends Window { emit: (e: eventWithTime) => undefined; } -const setup = function (this: ISuite, content: string): ISuite { +const setup = function ( + this: ISuite, + content: string, + canvasSample: 'all' | number = 'all', +): ISuite { const ctx = {} as ISuite; beforeAll(async () => { @@ -56,13 +65,16 @@ const setup = function (this: ISuite, content: string): ISuite { ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); - await ctx.page.evaluate(() => { + await ctx.page.evaluate((canvasSample) => { const { record } = ((window as unknown) as IWindow).rrweb; record({ recordCanvas: true, + sampling: { + canvas: canvasSample, + }, emit: ((window as unknown) as IWindow).emit, }); - }); + }, canvasSample); }); afterEach(async () => { @@ -257,4 +269,52 @@ describe('record webgl', function (this: ISuite) { assertSnapshot(ctx.events); expect(ctx.events.length).toEqual(5); }); + + describe('recordCanvas FPS', function (this: ISuite) { + jest.setTimeout(10_000); + + const maxFPS = 60; + + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + maxFPS, + ); + + it('should record snapshots', async () => { + await ctx.page.evaluate(() => { + const canvas = document.getElementById('canvas') as HTMLCanvasElement; + const gl = canvas.getContext('webgl', { preserveDrawingBuffer: true })!; + // Set the clear color to darkish green. + gl.clearColor(0.0, 0.5, 0.0, 1.0); + // Clear the context with the newly set color. This is + // the function call that actually does the drawing. + gl.clear(gl.COLOR_BUFFER_BIT); + }); + + await ctx.page.waitForTimeout(200); // give it some time buffer + + await ctx.page.evaluate(() => { + const canvas = document.getElementById('canvas') as HTMLCanvasElement; + const gl = canvas.getContext('webgl', { preserveDrawingBuffer: true })!; + // Set the clear color to darkish blue. + gl.clearColor(0.0, 0.0, 0.5, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + }); + + await ctx.page.waitForTimeout(200); + + await waitForRAF(ctx.page); + + // should yield a frame for each change at a max of 60fps + assertSnapshot(stripBase64(ctx.events)); + }); + }); }); diff --git a/packages/rrweb/test/replay/deserialize-args.test.ts b/packages/rrweb/test/replay/deserialize-args.test.ts index a39c439d1d..1343bd28b0 100644 --- a/packages/rrweb/test/replay/deserialize-args.test.ts +++ b/packages/rrweb/test/replay/deserialize-args.test.ts @@ -2,11 +2,10 @@ * @jest-environment jsdom */ +import { deserializeArg } from '../../src/replay/canvas/deserialize-args'; import { polyfillWebGLGlobals } from '../utils'; polyfillWebGLGlobals(); -import { deserializeArg } from '../../src/replay/canvas/webgl'; - let context: WebGLRenderingContext | WebGL2RenderingContext; describe('deserializeArg', () => { beforeEach(() => { @@ -14,7 +13,7 @@ describe('deserializeArg', () => { }); it('should deserialize Float32Array values', async () => { expect( - deserializeArg( + await deserializeArg( new Map(), context, )({ @@ -26,7 +25,7 @@ describe('deserializeArg', () => { it('should deserialize Float64Array values', async () => { expect( - deserializeArg( + await deserializeArg( new Map(), context, )({ @@ -39,7 +38,7 @@ describe('deserializeArg', () => { it('should deserialize ArrayBuffer values', async () => { const contents = [1, 2, 0, 4]; expect( - deserializeArg( + await deserializeArg( new Map(), context, )({ @@ -51,7 +50,7 @@ describe('deserializeArg', () => { it('should deserialize DataView values', async () => { expect( - deserializeArg( + await deserializeArg( new Map(), context, )({ @@ -70,7 +69,7 @@ describe('deserializeArg', () => { it('should leave arrays intact', async () => { const array = [1, 2, 3, 4]; - expect(deserializeArg(new Map(), context)(array)).toEqual(array); + expect(await deserializeArg(new Map(), context)(array)).toEqual(array); }); it('should deserialize complex objects', async () => { @@ -89,22 +88,20 @@ describe('deserializeArg', () => { 5, 6, ]; - expect(deserializeArg(new Map(), context)(serializedArg)).toStrictEqual([ - new DataView(new ArrayBuffer(16), 0, 16), - 5, - 6, - ]); + expect( + await deserializeArg(new Map(), context)(serializedArg), + ).toStrictEqual([new DataView(new ArrayBuffer(16), 0, 16), 5, 6]); }); it('should leave null as-is', async () => { - expect(deserializeArg(new Map(), context)(null)).toStrictEqual(null); + expect(await deserializeArg(new Map(), context)(null)).toStrictEqual(null); }); it('should support HTMLImageElements', async () => { const image = new Image(); image.src = 'http://example.com/image.png'; expect( - deserializeArg( + await deserializeArg( new Map(), context, )({ @@ -121,7 +118,7 @@ describe('deserializeArg', () => { imageMap.set(image.src, image); expect( - deserializeArg( + await deserializeArg( imageMap, context, )({ @@ -130,4 +127,77 @@ describe('deserializeArg', () => { }), ).toBe(image); }); + + it('should support blobs', async () => { + const arrayBuffer = new Uint8Array([1, 2, 0, 4]).buffer; + const expected = new Blob([arrayBuffer], { type: 'image/png' }); + + const deserialized = await deserializeArg( + new Map(), + context, + )({ + rr_type: 'Blob', + data: [ + { + rr_type: 'ArrayBuffer', + base64: 'AQIABA==', + }, + ], + type: 'image/png', + }); + + // `expect(blob).toEqual(otherBlob)` doesn't really do anything yet + // jest hasn't implemented a propper way to compare blobs + // more info: https://github.com/facebook/jest/issues/7372 + // because JSDOM doesn't support most functions needed for comparison: + // more info: https://github.com/jsdom/jsdom/issues/2555 + expect(deserialized).toEqual(expected); + // thats why we test size of the blob as well + expect(deserialized.size).toEqual(expected.size); + }); + + describe('isUnchanged', () => { + it('should set isUnchanged:true when non of the args are changed', async () => { + const status = { + isUnchanged: true, + }; + + await deserializeArg(new Map(), context, status)(true); + expect(status.isUnchanged).toBeTruthy(); + }); + + it('should set isUnchanged: false when args are deserialzed', async () => { + const status = { + isUnchanged: true, + }; + + await deserializeArg( + new Map(), + context, + status, + )({ + rr_type: 'Float64Array', + args: [[-1, -1, 3, -1, -1, 3]], + }); + expect(status.isUnchanged).toBeFalsy(); + }); + + it('should set isUnchanged: false when nested args are deserialzed', async () => { + const status = { + isUnchanged: true, + }; + + await deserializeArg( + new Map(), + context, + status, + )([ + { + rr_type: 'Float64Array', + args: [[-1, -1, 3, -1, -1, 3]], + }, + ]); + expect(status.isUnchanged).toBeFalsy(); + }); + }); }); diff --git a/packages/rrweb/test/replay/preload-all-images.test.ts b/packages/rrweb/test/replay/preload-all-images.test.ts index 4b4e0a4c71..de7ab4e2ee 100644 --- a/packages/rrweb/test/replay/preload-all-images.test.ts +++ b/packages/rrweb/test/replay/preload-all-images.test.ts @@ -8,7 +8,7 @@ import { Replayer } from '../../src/replay'; import {} from '../../src/types'; import { CanvasContext, - SerializedWebGlArg, + CanvasArg, IncrementalSource, EventType, eventWithTime, @@ -16,9 +16,7 @@ import { let replayer: Replayer; -const canvasMutationEventWithArgs = ( - args: SerializedWebGlArg[], -): eventWithTime => { +const canvasMutationEventWithArgs = (args: CanvasArg[]): eventWithTime => { return { timestamp: 100, type: EventType.IncrementalSnapshot, @@ -67,11 +65,11 @@ describe('preloadAllImages', () => { ); }); - it('should preload nested image', () => { + it('should preload nested image', async () => { replayer.service.state.context.events = [ canvasMutationEventWithArgs([ { - rr_type: 'something', + rr_type: 'Array', args: [ { rr_type: 'HTMLImageElement', @@ -82,7 +80,7 @@ describe('preloadAllImages', () => { ]), ]; - (replayer as any).preloadAllImages(); + await (replayer as any).preloadAllImages(); const expectedImage = new Image(); expectedImage.src = 'http://example.com'; diff --git a/packages/rrweb/test/replay/webgl-mutation.test.ts b/packages/rrweb/test/replay/webgl-mutation.test.ts index a8d6392c13..4f4ed75235 100644 --- a/packages/rrweb/test/replay/webgl-mutation.test.ts +++ b/packages/rrweb/test/replay/webgl-mutation.test.ts @@ -5,8 +5,9 @@ import { polyfillWebGLGlobals } from '../utils'; polyfillWebGLGlobals(); -import webglMutation, { variableListFor } from '../../src/replay/canvas/webgl'; +import webglMutation from '../../src/replay/canvas/webgl'; import { CanvasContext } from '../../src/types'; +import { variableListFor } from '../../src/replay/canvas/deserialize-args'; let canvas: HTMLCanvasElement; describe('webglMutation', () => { @@ -30,7 +31,7 @@ describe('webglMutation', () => { expect(variableListFor(context, 'WebGLShader')).toHaveLength(0); - webglMutation({ + await webglMutation({ mutation: { property: 'createShader', args: [35633], diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 69e72cd998..89f62c3fa4 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -220,6 +220,39 @@ export async function assertDomSnapshot( expect(stringifyDomSnapshot(data)).toMatchSnapshot(); } +export function stripBase64(events: eventWithTime[]) { + const base64Strings: string[] = []; + function walk(obj: T): T { + if (!obj || typeof obj !== 'object') return obj; + if (Array.isArray(obj)) return (obj.map((e) => walk(e)) as unknown) as T; + const newObj: Partial = {}; + for (let prop in obj) { + const value = obj[prop]; + if (prop === 'base64' && typeof value === 'string') { + let index = base64Strings.indexOf(value); + if (index === -1) { + index = base64Strings.push(value) - 1; + } + (newObj as any)[prop] = `base64-${index}`; + } else { + (newObj as any)[prop] = walk(value); + } + } + return newObj as T; + } + + return events.map((event) => { + if ( + event.type === EventType.IncrementalSnapshot && + event.data.source === IncrementalSource.CanvasMutation + ) { + const newData = walk(event.data); + return { ...event, data: newData }; + } + return event; + }); +} + const now = Date.now(); export const sampleEvents: eventWithTime[] = [ { diff --git a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts index 94a913e002..46635a453a 100644 --- a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts @@ -18,13 +18,15 @@ export declare class CanvasManager { lock(): void; unlock(): void; constructor(options: { - recordCanvas: boolean | number; + recordCanvas: boolean; mutationCb: canvasMutationCallback; win: IWindow; blockClass: blockClass; mirror: Mirror; + sampling?: 'all' | number; }); private processMutation; + private initCanvasFPSObserver; private initCanvasMutationObserver; private startPendingCanvasMutationFlusher; private startRAFTimestamping; diff --git a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts b/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts index fd1dfe4558..10337d4e2c 100644 --- a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts @@ -1,6 +1,6 @@ -import { IWindow, SerializedWebGlArg } from '../../../types'; -export declare function variableListFor(ctx: WebGLRenderingContext | WebGL2RenderingContext, ctor: string): any[]; -export declare const saveWebGLVar: (value: any, win: IWindow, ctx: WebGL2RenderingContext | WebGLRenderingContext) => number | void; -export declare function serializeArg(value: any, win: IWindow, ctx: WebGL2RenderingContext | WebGLRenderingContext): SerializedWebGlArg; -export declare const serializeArgs: (args: Array, win: IWindow, ctx: WebGLRenderingContext | WebGL2RenderingContext) => SerializedWebGlArg[]; +import { IWindow, CanvasArg } from '../../../types'; +export declare function variableListFor(ctx: RenderingContext, ctor: string): any[]; +export declare const saveWebGLVar: (value: any, win: IWindow, ctx: RenderingContext) => number | void; +export declare function serializeArg(value: any, win: IWindow, ctx: RenderingContext): CanvasArg; +export declare const serializeArgs: (args: Array, win: IWindow, ctx: RenderingContext) => CanvasArg[]; export declare const isInstanceOfWebGLObject: (value: any, win: IWindow) => value is WebGLTexture | WebGLShader | WebGLBuffer | WebGLVertexArrayObject | WebGLProgram | WebGLActiveInfo | WebGLUniformLocation | WebGLFramebuffer | WebGLRenderbuffer | WebGLShaderPrecisionFormat; diff --git a/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts b/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts new file mode 100644 index 0000000000..71ca36c5b2 --- /dev/null +++ b/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts @@ -0,0 +1,5 @@ +import { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse } from '../../types'; +export interface ImageBitmapDataURLRequestWorker { + postMessage: (message: ImageBitmapDataURLWorkerParams, transfer?: [ImageBitmap]) => void; + onmessage: (message: MessageEvent) => void; +} diff --git a/packages/rrweb/typings/replay/canvas/2d.d.ts b/packages/rrweb/typings/replay/canvas/2d.d.ts index 338cbf28f9..d780a7b981 100644 --- a/packages/rrweb/typings/replay/canvas/2d.d.ts +++ b/packages/rrweb/typings/replay/canvas/2d.d.ts @@ -6,4 +6,4 @@ export default function canvasMutation({ event, mutation, target, imageMap, erro target: HTMLCanvasElement; imageMap: Replayer['imageMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void; +}): Promise; diff --git a/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts b/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts new file mode 100644 index 0000000000..4d0c00e708 --- /dev/null +++ b/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts @@ -0,0 +1,7 @@ +import type { Replayer } from '../'; +import { CanvasArg, SerializedCanvasArg } from '../../types'; +export declare function variableListFor(ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext, ctor: string): any[]; +export declare function isSerializedArg(arg: unknown): arg is SerializedCanvasArg; +export declare function deserializeArg(imageMap: Replayer['imageMap'], ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext | null, preload?: { + isUnchanged: boolean; +}): (arg: CanvasArg) => Promise; diff --git a/packages/rrweb/typings/replay/canvas/index.d.ts b/packages/rrweb/typings/replay/canvas/index.d.ts index 72c6bdfce3..95b43accae 100644 --- a/packages/rrweb/typings/replay/canvas/index.d.ts +++ b/packages/rrweb/typings/replay/canvas/index.d.ts @@ -1,9 +1,10 @@ import { Replayer } from '..'; import { canvasMutationData } from '../../types'; -export default function canvasMutation({ event, mutation, target, imageMap, errorHandler, }: { +export default function canvasMutation({ event, mutation, target, imageMap, canvasEventMap, errorHandler, }: { event: Parameters[0]; mutation: canvasMutationData; target: HTMLCanvasElement; imageMap: Replayer['imageMap']; + canvasEventMap: Replayer['canvasEventMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void; +}): Promise; diff --git a/packages/rrweb/typings/replay/canvas/webgl.d.ts b/packages/rrweb/typings/replay/canvas/webgl.d.ts index 6e51941047..732b35a991 100644 --- a/packages/rrweb/typings/replay/canvas/webgl.d.ts +++ b/packages/rrweb/typings/replay/canvas/webgl.d.ts @@ -1,11 +1,9 @@ -import { Replayer } from '../'; -import { CanvasContext, canvasMutationCommand, SerializedWebGlArg } from '../../types'; -export declare function variableListFor(ctx: WebGLRenderingContext | WebGL2RenderingContext, ctor: string): any[]; -export declare function deserializeArg(imageMap: Replayer['imageMap'], ctx: WebGLRenderingContext | WebGL2RenderingContext): (arg: SerializedWebGlArg) => any; +import type { Replayer } from '../'; +import { CanvasContext, canvasMutationCommand } from '../../types'; export default function webglMutation({ mutation, target, type, imageMap, errorHandler, }: { mutation: canvasMutationCommand; target: HTMLCanvasElement; type: CanvasContext; imageMap: Replayer['imageMap']; errorHandler: Replayer['warnCanvasMutationFailed']; -}): void; +}): Promise; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index 3f0cc85d62..f298737c52 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -22,6 +22,7 @@ export declare class Replayer { private virtualStyleRulesMap; private cache; private imageMap; + private canvasEventMap; private mirror; private firstFullSnapshot; private newDocumentQueue; @@ -56,6 +57,7 @@ export declare class Replayer { private getImageArgs; private preloadAllImages; private preloadImages; + private deserializeAndPreloadCanvasEvents; private applyIncremental; private applyMutation; private applyScroll; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 913853afcf..03ce628bd6 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -124,6 +124,7 @@ export declare type SamplingStrategy = Partial<{ scroll: number; media: number; input: 'all' | 'last'; + canvas: 'all' | number; }>; export declare type RecordPlugin = { name: string; @@ -291,19 +292,24 @@ export declare enum CanvasContext { WebGL = 1, WebGL2 = 2 } -export declare type SerializedWebGlArg = { +export declare type SerializedCanvasArg = { rr_type: 'ArrayBuffer'; base64: string; +} | { + rr_type: 'Blob'; + data: Array; + type?: string; } | { rr_type: string; src: string; } | { rr_type: string; - args: SerializedWebGlArg[]; + args: Array; } | { rr_type: string; index: number; -} | string | number | boolean | null | SerializedWebGlArg[]; +}; +export declare type CanvasArg = SerializedCanvasArg | string | number | boolean | null | CanvasArg[]; declare type mouseInteractionParam = { type: MouseInteractions; id: number; @@ -361,6 +367,21 @@ export declare type canvasMutationWithType = { } & canvasMutationCommand; export declare type canvasMutationCallback = (p: canvasMutationParam) => void; export declare type canvasManagerMutationCallback = (target: HTMLCanvasElement, p: canvasMutationWithType) => void; +export declare type ImageBitmapDataURLWorkerParams = { + id: number; + bitmap: ImageBitmap; + width: number; + height: number; +}; +export declare type ImageBitmapDataURLWorkerResponse = { + id: number; +} | { + id: number; + type: string; + base64: string; + width: number; + height: number; +}; export declare type fontParam = { family: string; fontSource: string; diff --git a/yarn.lock b/yarn.lock index 6317759f87..19feb977df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1926,19 +1926,6 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31" integrity sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw== -"@rollup/plugin-commonjs@^11.0.0": - version "11.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz#60636c7a722f54b41e419e1709df05c7234557ef" - integrity sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA== - dependencies: - "@rollup/pluginutils" "^3.0.8" - commondir "^1.0.1" - estree-walker "^1.0.1" - glob "^7.1.2" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" - "@rollup/plugin-commonjs@^20.0.0": version "20.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz#3246872dcbcb18a54aaa6277a8c7d7f1b155b745" @@ -1952,6 +1939,19 @@ magic-string "^0.25.7" resolve "^1.17.0" +"@rollup/plugin-commonjs@^21.0.2": + version "21.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.2.tgz#0b9c539aa1837c94abfaf87945838b0fc8564891" + integrity sha512-d/OmjaLVO4j/aQX69bwpWPpbvI3TJkQuxoAk7BH8ew1PyoMBLTOuvJTjzG8oEoW7drIIqB0KCJtfFLu/2GClWg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + "@rollup/plugin-node-resolve@^13.0.4": version "13.0.6" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz#29629070bb767567be8157f575cfa8f2b8e9ef77" @@ -1964,6 +1964,18 @@ is-module "^1.0.0" resolve "^1.19.0" +"@rollup/plugin-node-resolve@^13.1.3": + version "13.1.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" + integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + "@rollup/plugin-node-resolve@^7.0.0": version "7.1.3" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" @@ -1991,14 +2003,6 @@ "@rollup/pluginutils" "^3.1.0" resolve "^1.17.0" -"@rollup/plugin-typescript@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.1.tgz#b7dc75ed6b4876e260b9e80624fab23bc98e4ac1" - integrity sha512-84rExe3ICUBXzqNX48WZV2Jp3OddjTMX97O2Py6D1KJaGSwWp0mDHXj+bCGNJqWHIEKDIT2U0sDjhP4czKi6cA== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - "@rollup/pluginutils@4", "@rollup/pluginutils@^4.1.0": version "4.1.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" @@ -2016,6 +2020,14 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751" + integrity sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -2248,6 +2260,11 @@ resolved "https://registry.yarnpkg.com/@types/nwsapi/-/nwsapi-2.2.2.tgz#1b1dccfc38b2b7e1b9ea71d5285796878375e862" integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g== +"@types/offscreencanvas@^2019.6.4": + version "2019.6.4" + resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz#64f6d120b53925028299c744fcdd32d2cd525963" + integrity sha512-u8SAgdZ8ROtkTF+mfZGOscl0or6BSj9A4g37e6nvxDc+YB/oDut0wHkK2PBBiC2bNR8TS0CPV+1gAk4fNisr1Q== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -2288,9 +2305,9 @@ "@types/node" "*" "@types/puppeteer@^5.4.4": - version "5.4.4" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.4.tgz#e92abeccc4f46207c3e1b38934a1246be080ccd0" - integrity sha512-3Nau+qi69CN55VwZb0ATtdUAlYlqOOQ3OfQfq0Hqgc4JMFXiQT/XInlwQ9g6LbicDslE6loIFsXFklGh5XmI6Q== + version "5.4.5" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.5.tgz#154e3850a77bfd3967f036680de8ddc88eb3a12b" + integrity sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA== dependencies: "@types/node" "*" @@ -2522,6 +2539,15 @@ resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.6.1.tgz#c92972b835540c4e3c5e14277f40dbcbdaee9571" integrity sha512-xYKDNuPR36/fUK+jmhM+oauBmbdUAfuJKnDjg3/7NbN+Pj03TX7e94LXnzkwGgAR+U/HWoMqM5UPTuGIYfIx9g== +"@yarn-tool/resolve-package@^1.0.40": + version "1.0.45" + resolved "https://registry.yarnpkg.com/@yarn-tool/resolve-package/-/resolve-package-1.0.45.tgz#4d9716a67903f46a76c8691eff546dafe55bf66f" + integrity sha512-xnfY8JceApkSTliZtr7X6yl1wZYhGbRp0beBMi1OtmvTVTm/ZSt3881Fw1M3ZwhHqr7OEfl8828LJK2q62BvoQ== + dependencies: + pkg-dir "< 6 >= 5" + tslib "^2.3.1" + upath2 "^3.1.12" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -5049,7 +5075,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^3.3.1: +find-cache-dir@^3.3.1, find-cache-dir@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -5073,6 +5099,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + findup-sync@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" @@ -5168,6 +5202,15 @@ fs-extra@8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -6197,7 +6240,7 @@ is-redirect@^1.0.0: resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= -is-reference@^1.1.2, is-reference@^1.2.1: +is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -8047,6 +8090,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -8127,7 +8177,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -magic-string@^0.25.2, magic-string@^0.25.7: +magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -9001,6 +9051,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -9015,6 +9072,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" @@ -9196,6 +9260,13 @@ path-is-inside@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= +path-is-network-drive@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/path-is-network-drive/-/path-is-network-drive-1.0.13.tgz#c9aa0183eb72c328aa83f43def93ddcb9d7ec4d4" + integrity sha512-Hg74mRN6mmXV+gTm3INjFK40ncAmC/Lo4qoQaSZ+GT3hZzlKdWQSqAjqyPeW0SvObP2W073WyYEBWY9d3wOm3A== + dependencies: + tslib "^2.3.1" + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -9211,6 +9282,13 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-strip-sep@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/path-strip-sep/-/path-strip-sep-1.0.10.tgz#2be4e789406b298af8709ff79af716134b733b98" + integrity sha512-JpCy+8LAJQQTO1bQsb/84s1g+/Stm3h39aOpPRBQ/paMUGVPPZChLTOTKHoaCkc/6sKuF7yVsnq5Pe1S6xQGcA== + dependencies: + tslib "^2.3.1" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -9304,6 +9382,13 @@ pixelmatch@^5.1.0: dependencies: pngjs "^4.0.1" +"pkg-dir@< 6 >= 5": + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -10231,7 +10316,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.20.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: +resolve@1.20.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -10332,10 +10417,10 @@ rollup-plugin-postcss@^3.1.1: safe-identifier "^0.4.1" style-inject "^0.3.0" -rollup-plugin-rename-node-modules@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-rename-node-modules/-/rollup-plugin-rename-node-modules-1.2.0.tgz#f1f1bb2192d1bbec258569bf6bda097002d7dbdf" - integrity sha512-IKsS3eJXHLAMXIndzNso9ijWJw1V3mqubRc2gb67v7VuLX9t41LObXqciM0JC3j7/WrHeptG47cejFU0qxXUJA== +rollup-plugin-rename-node-modules@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-rename-node-modules/-/rollup-plugin-rename-node-modules-1.3.1.tgz#d80091fc817ce726e7cdfc388ec2f4da286e280f" + integrity sha512-46TUPqO94GXuACYqVZjdbzNXTQAp+wTdZg/vUx2gaINb0da/ZPdaOtno2RGUOKBF4sbVM9v2ZqV98r4TQbp1UA== dependencies: estree-walker "^2.0.1" magic-string "^0.25.7" @@ -10369,6 +10454,23 @@ rollup-plugin-typescript2@^0.30.0: resolve "1.20.0" tslib "2.1.0" +rollup-plugin-typescript2@^0.31.2: + version "0.31.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.2.tgz#463aa713a7e2bf85b92860094b9f7fb274c5a4d8" + integrity sha512-hRwEYR1C8xDGVVMFJQdEVnNAeWRvpaY97g5mp3IeLnzhNXzSVq78Ye/BJ9PAaUfN4DXa/uDnqerifMOaMFY54Q== + dependencies: + "@rollup/pluginutils" "^4.1.2" + "@yarn-tool/resolve-package" "^1.0.40" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + resolve "^1.20.0" + tslib "^2.3.1" + +rollup-plugin-web-worker-loader@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-web-worker-loader/-/rollup-plugin-web-worker-loader-1.6.1.tgz#9d7a27575b64b0780fe4e8b3bc87470d217e485f" + integrity sha512-4QywQSz1NXFHKdyiou16mH3ijpcfLtLGOrAqvAqu1Gx+P8+zj+3gwC2BSL/VW1d+LW4nIHC8F7d7OXhs9UdR2A== + rollup-pluginutils@^2.8.2: version "2.8.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" @@ -10390,6 +10492,13 @@ rollup@^2.56.3: optionalDependencies: fsevents "~2.3.2" +rollup@^2.68.0: + version "2.68.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.68.0.tgz#6ccabfd649447f8f21d62bf41662e5caece3bd66" + integrity sha512-XrMKOYK7oQcTio4wyTz466mucnd8LzkiZLozZ4Rz0zQD+HeX4nUK4B8GrTX/2EvN2/vBF/i2WnaXboPxo0JylA== + optionalDependencies: + fsevents "~2.3.2" + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11620,6 +11729,15 @@ unzip-response@^2.0.1: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= +upath2@^3.1.12: + version "3.1.12" + resolved "https://registry.yarnpkg.com/upath2/-/upath2-3.1.12.tgz#441b3dfbadde21731017bd1b7beb169498efd0a9" + integrity sha512-yC3eZeCyCXFWjy7Nu4pgjLhXNYjuzuUmJiRgSSw6TJp8Emc+E4951HGPJf+bldFC5SL7oBLeNbtm1fGzXn2gxw== + dependencies: + path-is-network-drive "^1.0.13" + path-strip-sep "^1.0.10" + tslib "^2.3.1" + upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -11959,9 +12077,9 @@ ws@^6.1.0: async-limiter "~1.0.0" ws@^7.2.3: - version "7.5.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" - integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== ws@^7.4.3, ws@^7.4.5: version "7.5.3" @@ -12071,3 +12189,8 @@ yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From a0e664150575bf855bed2c4c941356a987108ced Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 5 May 2022 06:40:55 +0200 Subject: [PATCH 005/201] Test: Stylesheet append text node (#886) --- .../test/__snapshots__/record.test.ts.snap | 130 ++++++++++++++++++ packages/rrweb/test/record.test.ts | 20 +++ 2 files changed, 150 insertions(+) diff --git a/packages/rrweb/test/__snapshots__/record.test.ts.snap b/packages/rrweb/test/__snapshots__/record.test.ts.snap index 011db6fc59..74c951afa8 100644 --- a/packages/rrweb/test/__snapshots__/record.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/record.test.ts.snap @@ -94,6 +94,136 @@ exports[`record can add custom event 1`] = ` ]" `; +exports[`record captures inserted style text nodes correctly 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"style\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"div { color: red; }\\", + \\"isStyle\\": true, + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"section { color: blue; }\\", + \\"isStyle\\": true, + \\"id\\": 7 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\", + \\"size\\": \\"40\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 11 + } + ], + \\"id\\": 8 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 5, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"h1 { color: pink; }\\", + \\"isStyle\\": true, + \\"id\\": 12 + } + }, + { + \\"parentId\\": 5, + \\"nextId\\": 12, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"span { color: orange; }\\", + \\"isStyle\\": true, + \\"id\\": 13 + } + } + ] + } + } +]" +`; + exports[`record captures nested stylesheet rules 1`] = ` "[ { diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 8043bea6af..d213760485 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -344,6 +344,26 @@ describe('record', function (this: ISuite) { await ctx.page.waitForTimeout(50); assertSnapshot(ctx.events); }); + + it('captures inserted style text nodes correctly', async () => { + await ctx.page.evaluate(() => { + const { record } = ((window as unknown) as IWindow).rrweb; + + const styleEl = document.createElement(`style`); + styleEl.append(document.createTextNode('div { color: red; }')); + styleEl.append(document.createTextNode('section { color: blue; }')); + document.head.appendChild(styleEl); + + record({ + emit: ((window as unknown) as IWindow).emit, + }); + + styleEl.append(document.createTextNode('span { color: orange; }')); + styleEl.append(document.createTextNode('h1 { color: pink; }')); + }); + await waitForRAF(ctx.page); + assertSnapshot(ctx.events); + }); }); describe('record iframes', function (this: ISuite) { From 3cdcb8a12b959928d07af614cb5985007a679f02 Mon Sep 17 00:00:00 2001 From: Dmytro Kozlovskyi Date: Mon, 9 May 2022 17:28:36 +0300 Subject: [PATCH 006/201] Fix for issue #890 (#891) --- packages/rrweb/src/record/mutation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index f2e5ed69ba..aaa8900550 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -270,7 +270,7 @@ export default class MutationBuffer { // ensure shadowHost is a Node, or doc.contains will throw an error const notInDoc = !this.doc.contains(n) && - (rootShadowHost === null || !this.doc.contains(rootShadowHost)); + (!rootShadowHost || !this.doc.contains(rootShadowHost)); if (!n.parentNode || notInDoc) { return; } From 69499be6e201ed60d9b4e2db283ccb577822dd9f Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 9 May 2022 16:29:00 +0200 Subject: [PATCH 007/201] Perf: Apply the latest text mutation only (#885) * Perf: apply the latest text mutation only * Find unique text mutations in one iteration --- packages/rrweb/src/replay/index.ts | 6 ++++-- packages/rrweb/src/utils.ts | 20 ++++++++++++++++++++ packages/rrweb/typings/utils.d.ts | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index e3724d1b14..9c4fe9751a 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -52,6 +52,7 @@ import { getBaseDimension, hasShadowRoot, isSerializedIframe, + uniqueTextMutations, } from '../utils'; import getInjectStyleRules from './styles/inject-style'; import './styles/style.css'; @@ -179,9 +180,10 @@ export class Replayer { this.fragmentParentMap.forEach((parent, frag) => this.restoreRealParent(frag, parent), ); + // apply text needs to happen before virtual style rules gets applied // as it can overwrite the contents of a stylesheet - for (const d of mutationData.texts) { + for (const d of uniqueTextMutations(mutationData.texts)) { this.applyText(d, mutationData); } @@ -1613,7 +1615,7 @@ export class Replayer { Object.assign(this.legacy_missingNodeRetryMap, legacy_missingNodeMap); } - d.texts.forEach((mutation) => { + uniqueTextMutations(d.texts).forEach((mutation) => { let target = this.mirror.getNode(mutation.id); if (!target) { if (d.removes.find((r) => r.id === mutation.id)) { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index c60174133e..6a556f4aac 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -587,3 +587,23 @@ export function hasShadowRoot( ): n is T & { shadowRoot: ShadowRoot } { return Boolean(((n as unknown) as Element)?.shadowRoot); } + +/** + * Returns the latest mutation in the queue for each node. + * @param {textMutation[]} mutations The text mutations to filter. + * @returns {textMutation[]} The filtered text mutations. + */ +export function uniqueTextMutations(mutations: textMutation[]): textMutation[] { + const idSet = new Set(); + const uniqueMutations: textMutation[] = []; + + for (let i = mutations.length; i--; ) { + const mutation = mutations[i]; + if (!idSet.has(mutation.id)) { + uniqueMutations.push(mutation); + idSet.add(mutation.id); + } + } + + return uniqueMutations; +} diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index 5e5a26478b..fb6dcaba94 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -10,6 +10,7 @@ export declare function patch(source: { export declare function getWindowHeight(): number; export declare function getWindowWidth(): number; export declare function isBlocked(node: Node | null, blockClass: blockClass): boolean; +export declare function isSerialized(n: Node, mirror: Mirror): boolean; export declare function isIgnored(n: Node, mirror: Mirror): boolean; export declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean; export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent; @@ -62,4 +63,5 @@ export declare function getBaseDimension(node: Node, rootIframe: Node): Document export declare function hasShadowRoot(n: T): n is T & { shadowRoot: ShadowRoot; }; +export declare function getUniqueTextMutations(mutations: textMutation[]): textMutation[]; export {}; From de755ae577ac361dd1883a89d2597712b6a9cf93 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 12 May 2022 06:01:13 +0200 Subject: [PATCH 008/201] #853 Second try: fast-forward implementation v2: virtual dom optimization (#895) * rrdom: add a diff function for properties * implement diffChildren function and unit tests * finish basic functions of diff algorithm * fix several bugs in the diff algorithm * replace the virtual parent optimization in applyMutation() * fix: moveAndHover after the diff algorithm is executed * replace virtual style map with rrdom cssom version has to be above 0.5.0 to pass virtual style tests * fix: failed virtual style tests in replayer.test.ts * fix: failed polyfill tests caused by nodejs compatibility of different versions * fix: svg viewBox attribute doesn't work Cause the attribute viewBox is case sensitive, set value for viewbox doesn't work * feat: replace treeIndex optimization with rrdom * fix bug of diffProps and disable smooth scrolling animation in fast-forward mode * feat: add iframe support * fix: @rollup/plugin-typescript build errors in rrweb-player Error: @rollup/plugin-typescript TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error' * fix: bug when fast-forward input events and add test for it * add test for fast-forward scroll events * fix: custom style rules don't get inserted into some iframe elements * code style tweak * fix: enable to diff iframe elements * fix the jest error "Unexpected token 'export'" * try to fix build error of rrweb-player * correct the attributes definition in rrdom * fix: custom style rules are not inserted in some iframes * add support for shadow dom * add support for MediaInteraction * add canvas support * fix unit test error in rrdom * add support for Text, Comment * try to refactor RRDom * refactor RRDom to reduce duplicate code * rename document-browser to virtual-dom * increase the test coverage for document.ts and add ownerDocument for it * Merge branch 'master' into virtual-dom * add more test for virtual-dom.ts * use cssstyle in document-nodejs * fix: bundle error * improve document-nodejs * enable to diff scroll positions of an element * rename rrdom to virtualDom for more readability and make the tree public * revert unknown change * improve the css style parser for comments * improve code style * update typings * add handling for the case where legacy_missingNodeRetryMap is not empty * only import types from rrweb into rrdom * Apply suggestions from code review Co-authored-by: Justin Halsall * Apply suggestions from code review * fix building error in rrweb * add a method setDefaultSN to set a default value for a RRNode's __sn * fix rrweb test error and bump up other packages * add support for custom property of css styles * add a switch for virtual-dom optimization * Apply suggestions from code review 1. add an enum type for NodeType 2. rename nodeType from rrweb-snapshot to RRNodeType 3. rename notSerializedId to unserializedId 4. add comments for some confusing variables * adapt changes of #865 to virtual-dom and improve the test case for more coverage * apply review suggestions https://github.com/rrweb-io/rrweb/pull/853#pullrequestreview-922474953 * tweak the diff algorithm * add description of the flag useVirtualDom and remove outdated logConfig * Remove console.log * Contain changes to document * Upgrade rollup to 2.70.2 * Revert "Upgrade rollup to 2.70.2" This reverts commit b1be81a2a76565935c9dc391f31beb7f64d25956. * Fix type checking rrdom * Fix typing error while bundling * Fix tslib error on build Rollup would output the following error: `semantic error TS2343: This syntax requires an imported helper named '__spreadArray' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.` * Increase memory limit for rollup * Use esbuild for bundling Speeds up bundling significantly * Avoid circular dependencies and import un-bundled rrdom * Fix imports * Revert back to pre-esbuild This reverts the following commits: b7b3c8dbaa551a0129da1477136b1baaad28e6e1 72e23b8e27f9030d911358d3a17fe5ad1b3b5d4f 85d600a20c56cfa764cf1f858932ba14e67b1d23 61e1a5d323212ca8fbe0569e0b3062ddd53fc612 * Set node to lts (12 is no longer supported) * Speed up bundling and use less memory This fixes the out of memory errors happening while bundling * remove __sn from rrdom * fix typo * test: add a test case for StyleSheet mutation exceptions while fast-forwarding * rename Array.prototype.slice.call() to Array.from() * improve test cases * fix: PR #887 in 'virtual-dom' branch * apply justin's suggestion on 'Array.from' refactor related commit 0f6729d27a323260b36fbe79485a86715c0bc98a * improve import code structure Co-authored-by: Yun Feng --- .travis.yml | 2 +- .vscode/rrweb-monorepo.code-workspace | 3 +- guide.md | 2 + guide.zh_CN.md | 3 +- package.json | 3 + packages/rrdom/package.json | 17 +- packages/rrdom/rollup.config.js | 10 + packages/rrdom/src/diff.ts | 513 +++++++ packages/rrdom/src/document-nodejs.ts | 605 ++------ packages/rrdom/src/document.ts | 723 ++++++++++ packages/rrdom/src/polyfill.ts | 6 +- packages/rrdom/src/style.ts | 81 +- packages/rrdom/src/virtual-dom.ts | 450 ++++++ .../document-nodejs.test.ts.snap | 48 - .../__snapshots__/virtual-dom.test.ts.snap | 160 +++ packages/rrdom/test/diff.test.ts | 1250 +++++++++++++++++ packages/rrdom/test/document-nodejs.test.ts | 420 +++++- packages/rrdom/test/document.test.ts | 922 ++++++++++++ packages/rrdom/test/html/iframe.html | 31 + packages/rrdom/test/html/main.html | 8 +- packages/rrdom/test/html/shadow-dom.html | 20 + packages/rrdom/test/polyfill.test.ts | 50 +- packages/rrdom/test/util.ts | 19 - packages/rrdom/test/virtual-dom.test.ts | 550 ++++++++ packages/rrdom/tsconfig.json | 7 +- packages/rrweb-player/package.json | 12 +- packages/rrweb-player/rollup.config.js | 5 + packages/rrweb-player/tsconfig.json | 9 +- packages/rrweb-snapshot/src/rebuild.ts | 2 +- packages/rrweb-snapshot/src/types.ts | 22 + packages/rrweb-snapshot/src/utils.ts | 7 +- packages/rrweb-snapshot/typings/types.d.ts | 12 + packages/rrweb-snapshot/typings/utils.d.ts | 4 +- packages/rrweb/jest.config.js | 1 + packages/rrweb/package.json | 7 +- packages/rrweb/rollup.config.js | 53 +- packages/rrweb/src/packer/base.ts | 2 +- packages/rrweb/src/packer/unpack.ts | 4 +- .../rrweb/src/plugins/console/record/index.ts | 2 +- .../src/plugins/console/record/stringify.ts | 2 +- .../src/plugins/sequential-id/record/index.ts | 2 +- .../src/plugins/sequential-id/replay/index.ts | 2 +- packages/rrweb/src/record/iframe-manager.ts | 4 +- packages/rrweb/src/record/mutation.ts | 8 +- packages/rrweb/src/record/observer.ts | 8 +- .../rrweb/src/record/observers/canvas/2d.ts | 2 +- .../record/observers/canvas/canvas-manager.ts | 8 +- .../src/record/observers/canvas/canvas.ts | 4 +- .../record/observers/canvas/serialize-args.ts | 2 +- .../src/record/observers/canvas/webgl.ts | 6 +- .../rrweb/src/record/shadow-dom-manager.ts | 4 +- .../workers/image-bitmap-data-url-worker.ts | 2 +- packages/rrweb/src/replay/canvas/2d.ts | 4 +- .../src/replay/canvas/deserialize-args.ts | 2 +- packages/rrweb/src/replay/canvas/index.ts | 2 +- packages/rrweb/src/replay/canvas/webgl.ts | 5 +- packages/rrweb/src/replay/index.ts | 874 ++++++------ packages/rrweb/src/replay/virtual-styles.ts | 186 --- packages/rrweb/src/types.ts | 25 +- packages/rrweb/src/utils.ts | 243 +--- .../__snapshots__/integration.test.ts.snap | 38 +- .../test/__snapshots__/replayer.test.ts.snap | 4 +- packages/rrweb/test/e2e/webgl.test.ts | 11 +- packages/rrweb/test/events/iframe.ts | 48 +- packages/rrweb/test/events/input.ts | 215 +++ packages/rrweb/test/events/scroll.ts | 128 ++ packages/rrweb/test/events/shadow-dom.ts | 172 +++ .../test/events/style-sheet-rule-events.ts | 34 +- .../test/events/style-sheet-text-mutation.ts | 178 +++ packages/rrweb/test/integration.test.ts | 4 +- packages/rrweb/test/record.test.ts | 2 +- packages/rrweb/test/record/webgl.test.ts | 4 +- .../test/replay/preload-all-images.test.ts | 1 - .../rrweb/test/replay/virtual-styles.test.ts | 165 --- packages/rrweb/test/replay/webgl.test.ts | 4 +- packages/rrweb/test/replayer.test.ts | 273 +++- packages/rrweb/tsconfig.json | 5 +- packages/rrweb/typings/packer/base.d.ts | 2 +- .../typings/plugins/console/record/index.d.ts | 2 +- .../plugins/console/record/stringify.d.ts | 2 +- .../plugins/sequential-id/record/index.d.ts | 2 +- .../plugins/sequential-id/replay/index.d.ts | 2 +- .../rrweb/typings/record/iframe-manager.d.ts | 4 +- packages/rrweb/typings/record/mutation.d.ts | 2 +- .../typings/record/observers/canvas/2d.d.ts | 2 +- .../observers/canvas/canvas-manager.d.ts | 4 +- .../record/observers/canvas/canvas.d.ts | 2 +- .../observers/canvas/serialize-args.d.ts | 2 +- .../record/observers/canvas/webgl.d.ts | 2 +- .../typings/record/shadow-dom-manager.d.ts | 4 +- .../workers/image-bitmap-data-url-worker.d.ts | 2 +- packages/rrweb/typings/replay/canvas/2d.d.ts | 4 +- .../replay/canvas/deserialize-args.d.ts | 2 +- .../rrweb/typings/replay/canvas/index.d.ts | 2 +- packages/rrweb/typings/replay/index.d.ts | 12 +- .../rrweb/typings/replay/virtual-styles.d.ts | 42 - packages/rrweb/typings/types.d.ts | 21 +- packages/rrweb/typings/utils.d.ts | 50 +- yarn.lock | 1003 +++---------- 99 files changed, 7081 insertions(+), 2815 deletions(-) create mode 100644 packages/rrdom/src/diff.ts create mode 100644 packages/rrdom/src/document.ts create mode 100644 packages/rrdom/src/virtual-dom.ts delete mode 100644 packages/rrdom/test/__snapshots__/document-nodejs.test.ts.snap create mode 100644 packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap create mode 100644 packages/rrdom/test/diff.test.ts create mode 100644 packages/rrdom/test/document.test.ts create mode 100644 packages/rrdom/test/html/iframe.html create mode 100644 packages/rrdom/test/html/shadow-dom.html delete mode 100644 packages/rrdom/test/util.ts create mode 100644 packages/rrdom/test/virtual-dom.test.ts delete mode 100644 packages/rrweb/src/replay/virtual-styles.ts create mode 100644 packages/rrweb/test/events/input.ts create mode 100644 packages/rrweb/test/events/scroll.ts create mode 100644 packages/rrweb/test/events/shadow-dom.ts create mode 100644 packages/rrweb/test/events/style-sheet-text-mutation.ts delete mode 100644 packages/rrweb/test/replay/virtual-styles.test.ts delete mode 100644 packages/rrweb/typings/replay/virtual-styles.d.ts diff --git a/.travis.yml b/.travis.yml index 9d5753868e..9ecba610da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ os: linux dist: focal node_js: - - 12 + - lts/* install: - yarn diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 79849c8fe9..1d100ed367 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -24,8 +24,7 @@ "settings": { "jest.disabledWorkspaceFolders": [ " rrweb monorepo", - "rrweb-player (package)", - "rrdom (package)" + "rrweb-player (package)" ] } } diff --git a/guide.md b/guide.md index 3b2e058df1..1c52dcb8c0 100644 --- a/guide.md +++ b/guide.md @@ -299,10 +299,12 @@ The replayer accepts options as its constructor's second parameter, and it has t | insertStyleRules | [] | accepts multiple CSS rule string, which will be injected into the replay iframe | | triggerFocus | true | whether to trigger focus during replay | | UNSAFE_replayCanvas | false | whether to replay the canvas element. **Enable this will remove the sandbox, which is unsafe.** | +| pauseAnimation | true | whether to pause CSS animation when the replayer is paused | | mouseTail | true | whether to show mouse tail during replay. Set to false to disable mouse tail. A complete config can be found in this [type](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L407) | | unpackFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | | logConfig | - | configuration of console output playback, refer to the [console recipe](./docs/recipes/console.md) | | plugins | [] | load plugins to provide extended replay functions. [What is plugins?](./docs/recipes/plugin.md) | +| useVirtualDom | true | whether to use Virtual Dom optimization in the process of skipping to a new point of time | #### Use rrweb-player diff --git a/guide.zh_CN.md b/guide.zh_CN.md index a987934518..054fcb8b12 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -295,10 +295,11 @@ replayer.pause(5000); | insertStyleRules | [] | 可以传入多个 CSS rule string,用于自定义回放时 iframe 内的样式 | | triggerFocus | true | 回放时是否回放 focus 交互 | | UNSAFE_replayCanvas | false | 回放时是否回放 canvas 内容,**开启后将会关闭沙盒策略,导致一定风险** | +| pauseAnimation | true | 当播放器停止播放时,是否将 CSS 动画也停止播放 | | mouseTail | true | 是否在回放时增加鼠标轨迹。传入 false 可关闭,传入对象可以定制轨迹持续时间、样式等,配置详见[类型](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L407) | | unpackFn | - | 数据解压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| logConfig | - | console logger 数据播放设置,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) | | plugins | [] | 加载插件以获得额外的回放功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | +| useVirtualDom | true | 在播放器跳转到一个新的时间点的过程中,是否使用 Virtual Dom 优化 | #### 使用 rrweb-player diff --git a/package.json b/package.json index a9298f4111..342273e6c3 100644 --- a/package.json +++ b/package.json @@ -30,5 +30,8 @@ "test:watch": "yarn lerna run test:watch --parallel", "dev": "yarn lerna run dev --parallel", "repl": "cd packages/rrweb && npm run repl" + }, + "resolutions": { + "**/jsdom/cssom": "^0.5.0" } } diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index c7ce3a9c6b..bfe3459503 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -5,6 +5,7 @@ "dev": "rollup -c -w", "bundle": "rollup --config", "bundle:es-only": "cross-env ES_ONLY=true rollup --config", + "check-types": "tsc -noEmit", "test": "jest", "prepublish": "npm run bundle" }, @@ -27,18 +28,24 @@ "@rollup/plugin-commonjs": "^20.0.0", "@rollup/plugin-node-resolve": "^13.0.4", "@types/cssom": "^0.4.1", - "@types/jest": "^27.0.1", + "@types/cssstyle": "^2.2.1", + "@types/jest": "^27.4.1", "@types/nwsapi": "^2.2.2", - "jest": "^27.1.1", + "@types/puppeteer": "^5.4.4", + "compare-versions": "^4.1.3", + "jest": "^27.5.1", + "puppeteer": "^9.1.1", "rollup": "^2.56.3", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "^0.30.0", + "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-web-worker-loader": "^1.6.1", "rrweb-snapshot": "^1.1.14", - "ts-jest": "^27.0.5", - "typescript": "^3.9.5" + "ts-jest": "^27.1.3", + "typescript": "^4.6.2" }, "dependencies": { "cssom": "^0.5.0", + "cssstyle": "^2.3.0", "nwsapi": "^2.2.0" } } diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index 80baed3473..fe1d74a15b 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -2,6 +2,7 @@ import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import { terser } from 'rollup-plugin-terser'; import typescript from 'rollup-plugin-typescript2'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; import pkg from './package.json'; function toMinPath(path) { @@ -11,6 +12,10 @@ function toMinPath(path) { const basePlugins = [ resolve({ browser: true }), commonjs(), + + // supports bundling `web-worker:..filename` from rrweb + webWorkerLoader(), + typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, }), @@ -27,6 +32,11 @@ const baseConfigs = [ name: 'RRDocument', path: 'document-nodejs', }, + { + input: './src/virtual-dom.ts', + name: 'RRDocument', + path: 'virtual-dom', + }, ]; let configs = []; diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts new file mode 100644 index 0000000000..26f2309147 --- /dev/null +++ b/packages/rrdom/src/diff.ts @@ -0,0 +1,513 @@ +import { NodeType as RRNodeType, Mirror as NodeMirror } from 'rrweb-snapshot'; +import type { + canvasMutationData, + canvasEventWithTime, + inputData, + scrollData, +} from 'rrweb/src/types'; +import type { + IRRCDATASection, + IRRComment, + IRRDocument, + IRRDocumentType, + IRRElement, + IRRNode, + IRRText, +} from './document'; +import type { + RRCanvasElement, + RRElement, + RRIFrameElement, + RRMediaElement, + RRStyleElement, + RRDocument, + Mirror, +} from './virtual-dom'; + +const NAMESPACES: Record = { + svg: 'http://www.w3.org/2000/svg', + 'xlink:href': 'http://www.w3.org/1999/xlink', + xmlns: 'http://www.w3.org/2000/xmlns/', +}; + +// camel case svg element tag names +const SVGTagMap: Record = { + altglyph: 'altGlyph', + altglyphdef: 'altGlyphDef', + altglyphitem: 'altGlyphItem', + animatecolor: 'animateColor', + animatemotion: 'animateMotion', + animatetransform: 'animateTransform', + clippath: 'clipPath', + feblend: 'feBlend', + fecolormatrix: 'feColorMatrix', + fecomponenttransfer: 'feComponentTransfer', + fecomposite: 'feComposite', + feconvolvematrix: 'feConvolveMatrix', + fediffuselighting: 'feDiffuseLighting', + fedisplacementmap: 'feDisplacementMap', + fedistantlight: 'feDistantLight', + fedropshadow: 'feDropShadow', + feflood: 'feFlood', + fefunca: 'feFuncA', + fefuncb: 'feFuncB', + fefuncg: 'feFuncG', + fefuncr: 'feFuncR', + fegaussianblur: 'feGaussianBlur', + feimage: 'feImage', + femerge: 'feMerge', + femergenode: 'feMergeNode', + femorphology: 'feMorphology', + feoffset: 'feOffset', + fepointlight: 'fePointLight', + fespecularlighting: 'feSpecularLighting', + fespotlight: 'feSpotLight', + fetile: 'feTile', + feturbulence: 'feTurbulence', + foreignobject: 'foreignObject', + glyphref: 'glyphRef', + lineargradient: 'linearGradient', + radialgradient: 'radialGradient', +}; + +export type ReplayerHandler = { + mirror: NodeMirror; + applyCanvas: ( + canvasEvent: canvasEventWithTime, + canvasMutationData: canvasMutationData, + target: HTMLCanvasElement, + ) => void; + applyInput: (data: inputData) => void; + applyScroll: (data: scrollData, isSync: boolean) => void; +}; + +export function diff( + oldTree: Node, + newTree: IRRNode, + replayer: ReplayerHandler, + rrnodeMirror?: Mirror, +) { + const oldChildren = oldTree.childNodes; + const newChildren = newTree.childNodes; + rrnodeMirror = + rrnodeMirror || + (newTree as RRDocument).mirror || + (newTree.ownerDocument as RRDocument).mirror; + + if (oldChildren.length > 0 || newChildren.length > 0) { + diffChildren( + Array.from(oldChildren), + newChildren, + oldTree, + replayer, + rrnodeMirror, + ); + } + + let inputDataToApply = null, + scrollDataToApply = null; + switch (newTree.RRNodeType) { + case RRNodeType.Document: + const newRRDocument = newTree as IRRDocument; + scrollDataToApply = (newRRDocument as RRDocument).scrollData; + break; + case RRNodeType.Element: + const oldElement = (oldTree as Node) as HTMLElement; + const newRRElement = newTree as IRRElement; + diffProps(oldElement, newRRElement, rrnodeMirror); + scrollDataToApply = (newRRElement as RRElement).scrollData; + inputDataToApply = (newRRElement as RRElement).inputData; + switch (newRRElement.tagName) { + case 'AUDIO': + case 'VIDEO': + const oldMediaElement = (oldTree as Node) as HTMLMediaElement; + const newMediaRRElement = newRRElement as RRMediaElement; + if (newMediaRRElement.paused !== undefined) + newMediaRRElement.paused + ? oldMediaElement.pause() + : oldMediaElement.play(); + if (newMediaRRElement.muted !== undefined) + oldMediaElement.muted = newMediaRRElement.muted; + if (newMediaRRElement.volume !== undefined) + oldMediaElement.volume = newMediaRRElement.volume; + if (newMediaRRElement.currentTime !== undefined) + oldMediaElement.currentTime = newMediaRRElement.currentTime; + break; + case 'CANVAS': + (newTree as RRCanvasElement).canvasMutations.forEach( + (canvasMutation) => + replayer.applyCanvas( + canvasMutation.event, + canvasMutation.mutation, + (oldTree as Node) as HTMLCanvasElement, + ), + ); + break; + case 'STYLE': + applyVirtualStyleRulesToNode( + oldElement as HTMLStyleElement, + (newTree as RRStyleElement).rules, + ); + break; + } + if (newRRElement.shadowRoot) { + if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: 'open' }); + const oldChildren = oldElement.shadowRoot!.childNodes; + const newChildren = newRRElement.shadowRoot.childNodes; + if (oldChildren.length > 0 || newChildren.length > 0) + diffChildren( + Array.from(oldChildren), + newChildren, + oldElement.shadowRoot!, + replayer, + rrnodeMirror, + ); + } + break; + case RRNodeType.Text: + case RRNodeType.Comment: + case RRNodeType.CDATA: + if ( + oldTree.textContent !== + (newTree as IRRText | IRRComment | IRRCDATASection).data + ) + oldTree.textContent = (newTree as + | IRRText + | IRRComment + | IRRCDATASection).data; + break; + default: + } + + scrollDataToApply && replayer.applyScroll(scrollDataToApply, true); + /** + * Input data need to get applied after all children of this node are updated. + * Otherwise when we set a value for a select element whose options are empty, the value won't actually update. + */ + inputDataToApply && replayer.applyInput(inputDataToApply); + + // IFrame element doesn't have child nodes. + if (newTree.nodeName === 'IFRAME') { + const oldContentDocument = ((oldTree as Node) as HTMLIFrameElement) + .contentDocument; + const newIFrameElement = newTree as RRIFrameElement; + // If the iframe is cross-origin, the contentDocument will be null. + if (oldContentDocument) { + const sn = rrnodeMirror.getMeta(newIFrameElement.contentDocument); + if (sn) { + replayer.mirror.add(oldContentDocument, { ...sn }); + } + diff( + oldContentDocument, + newIFrameElement.contentDocument, + replayer, + rrnodeMirror, + ); + } + } +} + +function diffProps( + oldTree: HTMLElement, + newTree: IRRElement, + rrnodeMirror: Mirror, +) { + const oldAttributes = oldTree.attributes; + const newAttributes = newTree.attributes; + + for (const name in newAttributes) { + const newValue = newAttributes[name]; + const sn = rrnodeMirror.getMeta(newTree); + if (sn && 'isSVG' in sn && sn.isSVG && NAMESPACES[name]) + oldTree.setAttributeNS(NAMESPACES[name], name, newValue); + else if (newTree.tagName === 'CANVAS' && name === 'rr_dataURL') { + const image = document.createElement('img'); + image.src = newValue; + image.onload = () => { + const ctx = (oldTree as HTMLCanvasElement).getContext('2d'); + if (ctx) { + ctx.drawImage(image, 0, 0, image.width, image.height); + } + }; + } else oldTree.setAttribute(name, newValue); + } + + for (const { name } of Array.from(oldAttributes)) + if (!(name in newAttributes)) oldTree.removeAttribute(name); + + newTree.scrollLeft && (oldTree.scrollLeft = newTree.scrollLeft); + newTree.scrollTop && (oldTree.scrollTop = newTree.scrollTop); +} + +function diffChildren( + oldChildren: (Node | undefined)[], + newChildren: IRRNode[], + parentNode: Node, + replayer: ReplayerHandler, + rrnodeMirror: Mirror, +) { + let oldStartIndex = 0, + oldEndIndex = oldChildren.length - 1, + newStartIndex = 0, + newEndIndex = newChildren.length - 1; + let oldStartNode = oldChildren[oldStartIndex], + oldEndNode = oldChildren[oldEndIndex], + newStartNode = newChildren[newStartIndex], + newEndNode = newChildren[newEndIndex]; + let oldIdToIndex: Record | undefined = undefined, + indexInOld; + while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) { + if (oldStartNode === undefined) { + oldStartNode = oldChildren[++oldStartIndex]; + } else if (oldEndNode === undefined) { + oldEndNode = oldChildren[--oldEndIndex]; + } else if ( + replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newStartNode) + ) { + diff(oldStartNode, newStartNode, replayer, rrnodeMirror); + oldStartNode = oldChildren[++oldStartIndex]; + newStartNode = newChildren[++newStartIndex]; + } else if ( + replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newEndNode) + ) { + diff(oldEndNode, newEndNode, replayer, rrnodeMirror); + oldEndNode = oldChildren[--oldEndIndex]; + newEndNode = newChildren[--newEndIndex]; + } else if ( + replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newEndNode) + ) { + parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling); + diff(oldStartNode, newEndNode, replayer, rrnodeMirror); + oldStartNode = oldChildren[++oldStartIndex]; + newEndNode = newChildren[--newEndIndex]; + } else if ( + replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newStartNode) + ) { + parentNode.insertBefore(oldEndNode, oldStartNode); + diff(oldEndNode, newStartNode, replayer, rrnodeMirror); + oldEndNode = oldChildren[--oldEndIndex]; + newStartNode = newChildren[++newStartIndex]; + } else { + if (!oldIdToIndex) { + oldIdToIndex = {}; + for (let i = oldStartIndex; i <= oldEndIndex; i++) { + const oldChild = oldChildren[i]; + if (oldChild && replayer.mirror.hasNode(oldChild)) + oldIdToIndex[replayer.mirror.getId(oldChild)] = i; + } + } + indexInOld = oldIdToIndex[rrnodeMirror.getId(newStartNode)]; + if (indexInOld) { + const nodeToMove = oldChildren[indexInOld]!; + parentNode.insertBefore(nodeToMove, oldStartNode); + diff(nodeToMove, newStartNode, replayer, rrnodeMirror); + oldChildren[indexInOld] = undefined; + } else { + const newNode = createOrGetNode( + newStartNode, + replayer.mirror, + rrnodeMirror, + ); + + /** + * A mounted iframe element has an automatically created HTML element. + * We should delete it before insert a serialized one. Otherwise, an error 'Only one element on document allowed' will be thrown. + */ + if ( + replayer.mirror.getMeta(parentNode)?.type === RRNodeType.Document && + replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element && + ((parentNode as Node) as Document).documentElement + ) { + parentNode.removeChild( + ((parentNode as Node) as Document).documentElement, + ); + oldChildren[oldStartIndex] = undefined; + oldStartNode = undefined; + } + parentNode.insertBefore(newNode, oldStartNode || null); + diff(newNode, newStartNode, replayer, rrnodeMirror); + } + newStartNode = newChildren[++newStartIndex]; + } + } + if (oldStartIndex > oldEndIndex) { + const referenceRRNode = newChildren[newEndIndex + 1]; + let referenceNode = null; + if (referenceRRNode) + parentNode.childNodes.forEach((child) => { + if ( + replayer.mirror.getId(child) === rrnodeMirror.getId(referenceRRNode) + ) + referenceNode = child; + }); + for (; newStartIndex <= newEndIndex; ++newStartIndex) { + const newNode = createOrGetNode( + newChildren[newStartIndex], + replayer.mirror, + rrnodeMirror, + ); + parentNode.insertBefore(newNode, referenceNode); + diff(newNode, newChildren[newStartIndex], replayer, rrnodeMirror); + } + } else if (newStartIndex > newEndIndex) { + for (; oldStartIndex <= oldEndIndex; oldStartIndex++) { + const node = oldChildren[oldStartIndex]; + if (node) { + parentNode.removeChild(node); + replayer.mirror.removeNodeFromMap(node); + } + } + } +} + +export function createOrGetNode( + rrNode: IRRNode, + domMirror: NodeMirror, + rrnodeMirror: Mirror, +): Node { + let node = domMirror.getNode(rrnodeMirror.getId(rrNode)); + const sn = rrnodeMirror.getMeta(rrNode); + if (node !== null) return node; + switch (rrNode.RRNodeType) { + case RRNodeType.Document: + node = new Document(); + break; + case RRNodeType.DocumentType: + node = document.implementation.createDocumentType( + (rrNode as IRRDocumentType).name, + (rrNode as IRRDocumentType).publicId, + (rrNode as IRRDocumentType).systemId, + ); + break; + case RRNodeType.Element: + let tagName = (rrNode as IRRElement).tagName.toLowerCase(); + tagName = SVGTagMap[tagName] || tagName; + if (sn && 'isSVG' in sn && sn?.isSVG) { + node = document.createElementNS( + NAMESPACES['svg'], + (rrNode as IRRElement).tagName.toLowerCase(), + ); + } else node = document.createElement((rrNode as IRRElement).tagName); + break; + case RRNodeType.Text: + node = document.createTextNode((rrNode as IRRText).data); + break; + case RRNodeType.Comment: + node = document.createComment((rrNode as IRRComment).data); + break; + case RRNodeType.CDATA: + node = document.createCDATASection((rrNode as IRRCDATASection).data); + break; + } + + if (sn) domMirror.add(node, { ...sn }); + return node; +} + +export function getNestedRule( + rules: CSSRuleList, + position: number[], +): CSSGroupingRule { + const rule = rules[position[0]] as CSSGroupingRule; + if (position.length === 1) { + return rule; + } else { + return getNestedRule( + ((rule as CSSGroupingRule).cssRules[position[1]] as CSSGroupingRule) + .cssRules, + position.slice(2), + ); + } +} + +export enum StyleRuleType { + Insert, + Remove, + Snapshot, + SetProperty, + RemoveProperty, +} +type InsertRule = { + cssText: string; + type: StyleRuleType.Insert; + index?: number | number[]; +}; +type RemoveRule = { + type: StyleRuleType.Remove; + index: number | number[]; +}; +type SetPropertyRule = { + type: StyleRuleType.SetProperty; + index: number[]; + property: string; + value: string | null; + priority: string | undefined; +}; +type RemovePropertyRule = { + type: StyleRuleType.RemoveProperty; + index: number[]; + property: string; +}; + +export type VirtualStyleRules = Array< + InsertRule | RemoveRule | SetPropertyRule | RemovePropertyRule +>; + +export function getPositionsAndIndex(nestedIndex: number[]) { + const positions = [...nestedIndex]; + const index = positions.pop(); + return { positions, index }; +} + +export function applyVirtualStyleRulesToNode( + styleNode: HTMLStyleElement, + virtualStyleRules: VirtualStyleRules, +) { + const sheet = styleNode.sheet!; + + virtualStyleRules.forEach((rule) => { + if (rule.type === StyleRuleType.Insert) { + try { + if (Array.isArray(rule.index)) { + const { positions, index } = getPositionsAndIndex(rule.index); + const nestedRule = getNestedRule(sheet.cssRules, positions); + nestedRule.insertRule(rule.cssText, index); + } else { + sheet.insertRule(rule.cssText, rule.index); + } + } catch (e) { + /** + * sometimes we may capture rules with browser prefix + * insert rule with prefixs in other browsers may cause Error + */ + } + } else if (rule.type === StyleRuleType.Remove) { + try { + if (Array.isArray(rule.index)) { + const { positions, index } = getPositionsAndIndex(rule.index); + const nestedRule = getNestedRule(sheet.cssRules, positions); + nestedRule.deleteRule(index || 0); + } else { + sheet.deleteRule(rule.index); + } + } catch (e) { + /** + * accessing styleSheet rules may cause SecurityError + * for specific access control settings + */ + } + } else if (rule.type === StyleRuleType.SetProperty) { + const nativeRule = (getNestedRule( + sheet.cssRules, + rule.index, + ) as unknown) as CSSStyleRule; + nativeRule.style.setProperty(rule.property, rule.value, rule.priority); + } else if (rule.type === StyleRuleType.RemoveProperty) { + const nativeRule = (getNestedRule( + sheet.cssRules, + rule.index, + ) as unknown) as CSSStyleRule; + nativeRule.style.removeProperty(rule.property); + } + }); +} diff --git a/packages/rrdom/src/document-nodejs.ts b/packages/rrdom/src/document-nodejs.ts index aff8d90512..f7523ffefc 100644 --- a/packages/rrdom/src/document-nodejs.ts +++ b/packages/rrdom/src/document-nodejs.ts @@ -1,68 +1,24 @@ -import { INode, NodeType, serializedNodeWithId } from 'rrweb-snapshot'; -import { NWSAPI } from 'nwsapi'; -import { parseCSSText, camelize, toCSSText } from './style'; +import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import type { NWSAPI } from 'nwsapi'; +import type { CSSStyleDeclaration as CSSStyleDeclarationType } from 'cssstyle'; +import { + BaseRRCDATASectionImpl, + BaseRRCommentImpl, + BaseRRDocumentImpl, + BaseRRDocumentTypeImpl, + BaseRRElementImpl, + BaseRRMediaElementImpl, + BaseRRNode, + BaseRRTextImpl, + ClassList, + IRRDocument, + CSSStyleDeclaration, +} from './document'; const nwsapi = require('nwsapi'); const cssom = require('cssom'); +const cssstyle = require('cssstyle'); -export abstract class RRNode { - __sn: serializedNodeWithId | undefined; - children: Array = []; - parentElement: RRElement | null = null; - parentNode: RRNode | null = null; - ownerDocument: RRDocument | null = null; - ELEMENT_NODE = 1; - TEXT_NODE = 3; - - get firstChild() { - return this.children[0]; - } - - get nodeType() { - if (this instanceof RRDocument) return NodeType.Document; - if (this instanceof RRDocumentType) return NodeType.DocumentType; - if (this instanceof RRElement) return NodeType.Element; - if (this instanceof RRText) return NodeType.Text; - if (this instanceof RRCDATASection) return NodeType.CDATA; - if (this instanceof RRComment) return NodeType.Comment; - } - - get childNodes() { - return this.children; - } - - appendChild(newChild: RRNode): RRNode { - throw new Error( - `RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.`, - ); - } - - insertBefore(newChild: RRNode, refChild: RRNode | null): RRNode { - throw new Error( - `RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.`, - ); - } - - contains(node: RRNode) { - if (node === this) return true; - for (const child of this.children) { - if (child.contains(node)) return true; - } - return false; - } - - removeChild(node: RRNode) { - const indexOfChild = this.children.indexOf(node); - if (indexOfChild !== -1) { - this.children.splice(indexOfChild, 1); - node.parentElement = null; - node.parentNode = null; - } - } - - toString(nodeName?: string) { - return `${JSON.stringify(this.__sn?.id) || ''} ${nodeName}`; - } -} +export class RRNode extends BaseRRNode {} export class RRWindow { scrollLeft = 0; @@ -74,8 +30,10 @@ export class RRWindow { } } -export class RRDocument extends RRNode { - private mirror: Map = new Map(); +export class RRDocument + extends BaseRRDocumentImpl(RRNode) + implements IRRDocument { + readonly nodeName: '#document' = '#document'; private _nwsapi: NWSAPI; get nwsapi() { if (!this._nwsapi) { @@ -95,66 +53,32 @@ export class RRDocument extends RRNode { return this._nwsapi; } - get documentElement(): RRElement { - return this.children.find( - (node) => node instanceof RRElement && node.tagName === 'HTML', - ) as RRElement; + get documentElement(): RRElement | null { + return super.documentElement as RRElement | null; } - get body() { - return ( - this.documentElement?.children.find( - (node) => node instanceof RRElement && node.tagName === 'BODY', - ) || null - ); + get body(): RRElement | null { + return super.body as RRElement | null; } get head() { - return ( - this.documentElement?.children.find( - (node) => node instanceof RRElement && node.tagName === 'HEAD', - ) || null - ); + return super.head as RRElement | null; } - get implementation() { + get implementation(): RRDocument { return this; } - get firstElementChild() { - return this.documentElement; + get firstElementChild(): RRElement | null { + return this.documentElement as RRElement | null; } appendChild(childNode: RRNode) { - const nodeType = childNode.nodeType; - if (nodeType === NodeType.Element || nodeType === NodeType.DocumentType) { - if (this.children.some((s) => s.nodeType === nodeType)) { - throw new Error( - `RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one ${ - nodeType === NodeType.Element ? 'RRElement' : 'RRDoctype' - } on RRDocument allowed.`, - ); - } - } - childNode.parentElement = null; - childNode.parentNode = this; - childNode.ownerDocument = this; - this.children.push(childNode); - return childNode; + return super.appendChild(childNode); } insertBefore(newChild: RRNode, refChild: RRNode | null) { - if (refChild === null) return this.appendChild(newChild); - const childIndex = this.children.indexOf(refChild); - if (childIndex == -1) - throw new Error( - "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", - ); - this.children.splice(childIndex, 0, newChild); - newChild.parentElement = null; - newChild.parentNode = this; - newChild.ownerDocument = this; - return newChild; + return super.insertBefore(newChild, refChild); } querySelectorAll(selectors: string): RRNode[] { @@ -216,16 +140,16 @@ export class RRDocument extends RRNode { element = new RRMediaElement(upperTagName); break; case 'IFRAME': - element = new RRIframeElement(upperTagName); + element = new RRIFrameElement(upperTagName); break; case 'IMG': - element = new RRImageElement('IMG'); + element = new RRImageElement(upperTagName); break; case 'CANVAS': - element = new RRCanvasElement('CANVAS'); + element = new RRCanvasElement(upperTagName); break; case 'STYLE': - element = new RRStyleElement('STYLE'); + element = new RRStyleElement(upperTagName); break; default: element = new RRElement(upperTagName); @@ -235,10 +159,7 @@ export class RRDocument extends RRNode { return element; } - createElementNS( - _namespaceURI: 'http://www.w3.org/2000/svg', - qualifiedName: string, - ) { + createElementNS(_namespaceURI: string, qualifiedName: string) { return this.createElement(qualifiedName as keyof HTMLElementTagNameMap); } @@ -259,266 +180,40 @@ export class RRDocument extends RRNode { textNode.ownerDocument = this; return textNode; } - - /** - * This does come with some side effects. For example: - * 1. All event listeners currently registered on the document, nodes inside the document, or the document's window are removed. - * 2. All existing nodes are removed from the document. - */ - open() { - this.children = []; - } - - close() {} - - buildFromDom(dom: Document) { - let notSerializedId = -1; - const NodeTypeMap: Record = {}; - NodeTypeMap[document.DOCUMENT_NODE] = NodeType.Document; - NodeTypeMap[document.DOCUMENT_TYPE_NODE] = NodeType.DocumentType; - NodeTypeMap[document.ELEMENT_NODE] = NodeType.Element; - NodeTypeMap[document.TEXT_NODE] = NodeType.Text; - NodeTypeMap[document.CDATA_SECTION_NODE] = NodeType.CDATA; - NodeTypeMap[document.COMMENT_NODE] = NodeType.Comment; - - function getValidTagName(element: HTMLElement): string { - if (element instanceof HTMLFormElement) { - return 'FORM'; - } - return element.tagName.toUpperCase().trim(); - } - - const walk = function (node: INode) { - let serializedNodeWithId = node.__sn; - let rrNode: RRNode; - if (!serializedNodeWithId) { - serializedNodeWithId = { - type: NodeTypeMap[node.nodeType], - textContent: '', - id: notSerializedId, - }; - notSerializedId -= 1; - node.__sn = serializedNodeWithId; - } - if (!this.mirror.has(serializedNodeWithId.id)) { - switch (node.nodeType) { - case node.DOCUMENT_NODE: - if ( - serializedNodeWithId.rootId && - serializedNodeWithId.rootId !== serializedNodeWithId.id - ) - rrNode = this.createDocument(); - else rrNode = this; - break; - case node.DOCUMENT_TYPE_NODE: - const documentType = (node as unknown) as DocumentType; - rrNode = this.createDocumentType( - documentType.name, - documentType.publicId, - documentType.systemId, - ); - break; - case node.ELEMENT_NODE: - const elementNode = (node as unknown) as HTMLElement; - const tagName = getValidTagName(elementNode); - rrNode = this.createElement(tagName); - const rrElement = rrNode as RRElement; - for (const { name, value } of Array.from(elementNode.attributes)) { - rrElement.attributes[name] = value; - } - // form fields - if ( - tagName === 'INPUT' || - tagName === 'TEXTAREA' || - tagName === 'SELECT' - ) { - const value = (elementNode as - | HTMLInputElement - | HTMLTextAreaElement).value; - if ( - ['RADIO', 'CHECKBOX', 'SUBMIT', 'BUTTON'].includes( - rrElement.attributes.type as string, - ) && - value - ) { - rrElement.attributes.value = value; - } else if ((elementNode as HTMLInputElement).checked) { - rrElement.attributes.checked = (elementNode as HTMLInputElement).checked; - } - } - if (tagName === 'OPTION') { - const selectValue = (elementNode as HTMLOptionElement) - .parentElement; - if ( - rrElement.attributes.value === - (selectValue as HTMLSelectElement).value - ) { - rrElement.attributes.selected = (elementNode as HTMLOptionElement).selected; - } - } - // canvas image data - if (tagName === 'CANVAS') { - rrElement.attributes.rr_dataURL = (elementNode as HTMLCanvasElement).toDataURL(); - } - // media elements - if (tagName === 'AUDIO' || tagName === 'VIDEO') { - const rrMediaElement = rrElement as RRMediaElement; - rrMediaElement.paused = (elementNode as HTMLMediaElement).paused; - rrMediaElement.currentTime = (elementNode as HTMLMediaElement).currentTime; - } - // scroll - if (elementNode.scrollLeft) { - rrElement.scrollLeft = elementNode.scrollLeft; - } - if (elementNode.scrollTop) { - rrElement.scrollTop = elementNode.scrollTop; - } - break; - case node.TEXT_NODE: - rrNode = this.createTextNode( - ((node as unknown) as Text).textContent, - ); - break; - case node.CDATA_SECTION_NODE: - rrNode = this.createCDATASection(); - break; - case node.COMMENT_NODE: - rrNode = this.createComment( - ((node as unknown) as Comment).textContent || '', - ); - break; - default: - return; - } - rrNode.__sn = serializedNodeWithId; - this.mirror.set(serializedNodeWithId.id, rrNode); - } else { - rrNode = this.mirror.get(serializedNodeWithId.id); - rrNode.parentElement = null; - rrNode.parentNode = null; - rrNode.children = []; - } - const parentNode = node.parentElement || node.parentNode; - if (parentNode) { - const parentSN = ((parentNode as unknown) as INode).__sn; - const parentRRNode = this.mirror.get(parentSN.id); - parentRRNode.appendChild(rrNode); - rrNode.parentNode = parentRRNode; - rrNode.parentElement = - parentRRNode instanceof RRElement ? parentRRNode : null; - } - - if ( - serializedNodeWithId.type === NodeType.Document || - serializedNodeWithId.type === NodeType.Element - ) { - node.childNodes.forEach((node) => walk((node as unknown) as INode)); - } - }.bind(this); - - if (dom) { - this.destroyTree(); - walk((dom as unknown) as INode); - } - } - - destroyTree() { - this.children = []; - this.mirror.clear(); - } - - toString() { - return super.toString('RRDocument'); - } } -export class RRDocumentType extends RRNode { - readonly name: string; - readonly publicId: string; - readonly systemId: string; - - constructor(qualifiedName: string, publicId: string, systemId: string) { - super(); - this.name = qualifiedName; - this.publicId = publicId; - this.systemId = systemId; - } - - toString() { - return super.toString('RRDocumentType'); - } -} - -export class RRElement extends RRNode { - tagName: string; - attributes: Record = {}; - scrollLeft: number = 0; - scrollTop: number = 0; - shadowRoot: RRElement | null = null; +export class RRDocumentType extends BaseRRDocumentTypeImpl(RRNode) {} +export class RRElement extends BaseRRElementImpl(RRNode) { + private _style: CSSStyleDeclarationType; constructor(tagName: string) { - super(); - this.tagName = tagName; - } - - get classList() { - return new ClassList( - this.attributes.class as string | undefined, - (newClassName) => { - this.attributes.class = newClassName; + super(tagName); + this._style = new cssstyle.CSSStyleDeclaration(); + const style = this._style; + Object.defineProperty(this.attributes, 'style', { + get() { + return style.cssText; }, - ); - } - - get id() { - return this.attributes.id; - } - - get className() { - return this.attributes.class || ''; + set(cssText: string) { + style.cssText = cssText; + }, + }); } - get textContent() { - return ''; + get style() { + return (this._style as unknown) as CSSStyleDeclaration; } - set textContent(newText: string) {} - - get style() { - const style = (this.attributes.style - ? parseCSSText(this.attributes.style as string) - : {}) as Record & { - setProperty: ( - name: string, - value: string | null, - priority?: string | null, - ) => void; - }; - style.setProperty = (name: string, value: string | null) => { - const normalizedName = camelize(name); - if (!value) delete style[normalizedName]; - else style[normalizedName] = value; - this.attributes.style = toCSSText(style); - }; - // This is used to bypass the smoothscroll polyfill in rrweb player. - style.scrollBehavior = ''; - return style; + attachShadow(_init: ShadowRootInit): RRElement { + return super.attachShadow(_init) as RRElement; } - get firstElementChild(): RRElement | null { - for (let child of this.children) - if (child instanceof RRElement) return child; - return null; + appendChild(newChild: RRNode): RRNode { + return super.appendChild(newChild) as RRNode; } - get nextElementSibling(): RRElement | null { - let parentNode = this.parentNode; - if (!parentNode) return null; - const siblings = parentNode.children; - let index = siblings.indexOf(this); - for (let i = index + 1; i < siblings.length; i++) - if (siblings[i] instanceof RRElement) return siblings[i] as RRElement; - return null; + insertBefore(newChild: RRNode, refChild: RRNode | null): RRNode { + return super.insertBefore(newChild, refChild) as RRNode; } getAttribute(name: string) { @@ -531,57 +226,44 @@ export class RRElement extends RRNode { this.attributes[name.toLowerCase()] = attribute; } - hasAttribute(name: string) { - return (name && name.toLowerCase()) in this.attributes; - } - - setAttributeNS( - _namespace: string | null, - qualifiedName: string, - value: string, - ): void { - this.setAttribute(qualifiedName, value); - } - removeAttribute(name: string) { - delete this.attributes[name]; + delete this.attributes[name.toLowerCase()]; } - appendChild(newChild: RRNode): RRNode { - this.children.push(newChild); - newChild.parentNode = this; - newChild.parentElement = this; - newChild.ownerDocument = this.ownerDocument; - return newChild; + get firstElementChild(): RRElement | null { + for (let child of this.childNodes) + if (child.RRNodeType === RRNodeType.Element) return child as RRElement; + return null; } - insertBefore(newChild: RRNode, refChild: RRNode | null): RRNode { - if (refChild === null) return this.appendChild(newChild); - const childIndex = this.children.indexOf(refChild); - if (childIndex == -1) - throw new Error( - "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", - ); - this.children.splice(childIndex, 0, newChild); - newChild.parentElement = null; - newChild.parentNode = this; - newChild.ownerDocument = this.ownerDocument; - return newChild; + get nextElementSibling(): RRElement | null { + let parentNode = this.parentNode; + if (!parentNode) return null; + const siblings = parentNode.childNodes; + let index = siblings.indexOf(this); + for (let i = index + 1; i < siblings.length; i++) + if (siblings[i] instanceof RRElement) return siblings[i] as RRElement; + return null; } querySelectorAll(selectors: string): RRNode[] { + const result: RRElement[] = []; if (this.ownerDocument !== null) { - return (this.ownerDocument.nwsapi.select( + ((this.ownerDocument as RRDocument).nwsapi.select( selectors, (this as unknown) as Element, + (element) => { + if (((element as unknown) as RRElement) !== this) + result.push((element as unknown) as RRElement); + }, ) as unknown) as RRNode[]; } - return []; + return result; } getElementById(elementId: string): RRElement | null { - if (this instanceof RRElement && this.id === elementId) return this; - for (const child of this.children) { + if (this.id === elementId) return this; + for (const child of this.childNodes) { if (child instanceof RRElement) { const result = child.getElementById(elementId); if (result !== null) return result; @@ -596,12 +278,12 @@ export class RRElement extends RRNode { // Make sure this element has all queried class names. if ( this instanceof RRElement && - queryClassList.filter((queriedClassName) => - this.classList.some((name) => name === queriedClassName), - ).length == queryClassList.length + queryClassList.classes.filter((queriedClassName) => + this.classList.classes.some((name) => name === queriedClassName), + ).length == queryClassList.classes.length ) elements.push(this); - for (const child of this.children) { + for (const child of this.childNodes) { if (child instanceof RRElement) elements = elements.concat(child.getElementsByClassName(className)); } @@ -613,32 +295,12 @@ export class RRElement extends RRNode { const normalizedTagName = tagName.toUpperCase(); if (this instanceof RRElement && this.tagName === normalizedTagName) elements.push(this); - for (const child of this.children) { + for (const child of this.childNodes) { if (child instanceof RRElement) elements = elements.concat(child.getElementsByTagName(tagName)); } return elements; } - - dispatchEvent(_event: Event) { - return true; - } - - /** - * Creates a shadow root for element and returns it. - */ - attachShadow(init: ShadowRootInit): RRElement { - this.shadowRoot = init.mode === 'open' ? this : null; - return this; - } - - toString() { - let attributeString = ''; - for (let attribute in this.attributes) { - attributeString += `${attribute}="${this.attributes[attribute]}" `; - } - return `${super.toString(this.tagName)} ${attributeString}`; - } } export class RRImageElement extends RRElement { @@ -648,16 +310,7 @@ export class RRImageElement extends RRElement { onload: ((this: GlobalEventHandlers, ev: Event) => any) | null; } -export class RRMediaElement extends RRElement { - currentTime: number = 0; - paused: boolean = true; - async play() { - this.paused = false; - } - async pause() { - this.paused = true; - } -} +export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} export class RRCanvasElement extends RRElement { /** @@ -675,7 +328,7 @@ export class RRStyleElement extends RRElement { if (!this._sheet) { let result = ''; for (let child of this.childNodes) - if (child.nodeType === NodeType.Text) + if (child.RRNodeType === RRNodeType.Text) result += (child as RRText).textContent; this._sheet = cssom.parse(result); } @@ -683,7 +336,7 @@ export class RRStyleElement extends RRElement { } } -export class RRIframeElement extends RRElement { +export class RRIFrameElement extends RRElement { width: string = ''; height: string = ''; src: string = ''; @@ -699,89 +352,27 @@ export class RRIframeElement extends RRElement { } } -export class RRText extends RRNode { - textContent: string; - - constructor(data: string) { - super(); - this.textContent = data; - } - - toString() { - return `${super.toString('RRText')} text=${JSON.stringify( - this.textContent, - )}`; - } +export class RRText extends BaseRRTextImpl(RRNode) { + readonly nodeName: '#text' = '#text'; } -export class RRComment extends RRNode { - data: string; - - constructor(data: string) { - super(); - this.data = data; - } - - toString() { - return `${super.toString('RRComment')} data=${JSON.stringify(this.data)}`; - } +export class RRComment extends BaseRRCommentImpl(RRNode) { + readonly nodeName: '#comment' = '#comment'; } -export class RRCDATASection extends RRNode { - data: string; - - constructor(data: string) { - super(); - this.data = data; - } - toString() { - return `${super.toString('RRCDATASection')} data=${JSON.stringify( - this.data, - )}`; - } +export class RRCDATASection extends BaseRRCDATASectionImpl(RRNode) { + readonly nodeName: '#cdata-section' = '#cdata-section'; } interface RRElementTagNameMap { - img: RRImageElement; audio: RRMediaElement; + canvas: RRCanvasElement; + iframe: RRIFrameElement; + img: RRImageElement; + style: RRStyleElement; video: RRMediaElement; } type RRElementType< K extends keyof HTMLElementTagNameMap > = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; - -class ClassList extends Array { - private onChange: ((newClassText: string) => void) | undefined; - - constructor( - classText?: string, - onChange?: ((newClassText: string) => void) | undefined, - ) { - super(); - if (classText) { - const classes = classText.trim().split(/\s+/); - super.push(...classes); - } - this.onChange = onChange; - } - - add = (...classNames: string[]) => { - for (const item of classNames) { - const className = String(item); - if (super.indexOf(className) >= 0) continue; - super.push(className); - } - this.onChange && this.onChange(super.join(' ')); - }; - - remove = (...classNames: string[]) => { - for (const item of classNames) { - const className = String(item); - const index = super.indexOf(className); - if (index < 0) continue; - super.splice(index, 1); - } - this.onChange && this.onChange(super.join(' ')); - }; -} diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts new file mode 100644 index 0000000000..aec54d9a98 --- /dev/null +++ b/packages/rrdom/src/document.ts @@ -0,0 +1,723 @@ +import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { parseCSSText, camelize, toCSSText } from './style'; +export interface IRRNode { + parentElement: IRRNode | null; + parentNode: IRRNode | null; + childNodes: IRRNode[]; + ownerDocument: IRRDocument; + readonly ELEMENT_NODE: number; + readonly TEXT_NODE: number; + // corresponding nodeType value of standard HTML Node + readonly nodeType: number; + readonly nodeName: string; // https://dom.spec.whatwg.org/#dom-node-nodename + readonly RRNodeType: RRNodeType; + + firstChild: IRRNode | null; + + lastChild: IRRNode | null; + + nextSibling: IRRNode | null; + + textContent: string | null; + + contains(node: IRRNode): boolean; + + appendChild(newChild: IRRNode): IRRNode; + + insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode; + + removeChild(node: IRRNode): IRRNode; + + toString(): string; +} +export interface IRRDocument extends IRRNode { + documentElement: IRRElement | null; + + body: IRRElement | null; + + head: IRRElement | null; + + implementation: IRRDocument; + + firstElementChild: IRRElement | null; + + readonly nodeName: '#document'; + + compatMode: 'BackCompat' | 'CSS1Compat'; + + createDocument( + _namespace: string | null, + _qualifiedName: string | null, + _doctype?: DocumentType | null, + ): IRRDocument; + + createDocumentType( + qualifiedName: string, + publicId: string, + systemId: string, + ): IRRDocumentType; + + createElement(tagName: string): IRRElement; + + createElementNS(_namespaceURI: string, qualifiedName: string): IRRElement; + + createTextNode(data: string): IRRText; + + createComment(data: string): IRRComment; + + createCDATASection(data: string): IRRCDATASection; + + open(): void; + + close(): void; + + write(content: string): void; +} +export interface IRRElement extends IRRNode { + tagName: string; + attributes: Record; + shadowRoot: IRRElement | null; + scrollLeft?: number; + scrollTop?: number; + id: string; + className: string; + classList: ClassList; + style: CSSStyleDeclaration; + + attachShadow(init: ShadowRootInit): IRRElement; + + getAttribute(name: string): string | null; + + setAttribute(name: string, attribute: string): void; + + setAttributeNS( + namespace: string | null, + qualifiedName: string, + value: string, + ): void; + + removeAttribute(name: string): void; + + dispatchEvent(event: Event): boolean; +} +export interface IRRDocumentType extends IRRNode { + readonly name: string; + readonly publicId: string; + readonly systemId: string; +} +export interface IRRText extends IRRNode { + readonly nodeName: '#text'; + data: string; +} +export interface IRRComment extends IRRNode { + readonly nodeName: '#comment'; + data: string; +} +export interface IRRCDATASection extends IRRNode { + readonly nodeName: '#cdata-section'; + data: string; +} + +type ConstrainedConstructor = new (...args: any[]) => T; + +/** + * This is designed as an abstract class so it should never be instantiated. + */ +export class BaseRRNode implements IRRNode { + public childNodes: IRRNode[] = []; + public parentElement: IRRNode | null = null; + public parentNode: IRRNode | null = null; + public textContent: string | null; + public ownerDocument: IRRDocument; + public readonly ELEMENT_NODE: number = NodeType.ELEMENT_NODE; + public readonly TEXT_NODE: number = NodeType.TEXT_NODE; + // corresponding nodeType value of standard HTML Node + public readonly nodeType: number; + public readonly nodeName: string; + public readonly RRNodeType: RRNodeType; + + constructor(...args: any[]) {} + + public get firstChild(): IRRNode | null { + return this.childNodes[0] || null; + } + + public get lastChild(): IRRNode | null { + return this.childNodes[this.childNodes.length - 1] || null; + } + + public get nextSibling(): IRRNode | null { + let parentNode = this.parentNode; + if (!parentNode) return null; + const siblings = parentNode.childNodes; + let index = siblings.indexOf(this); + return siblings[index + 1] || null; + } + + public contains(node: IRRNode) { + if (node === this) return true; + for (const child of this.childNodes) { + if (child.contains(node)) return true; + } + return false; + } + + public appendChild(_newChild: IRRNode): IRRNode { + throw new Error( + `RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.`, + ); + } + + public insertBefore(_newChild: IRRNode, _refChild: IRRNode | null): IRRNode { + throw new Error( + `RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.`, + ); + } + + public removeChild(node: IRRNode): IRRNode { + throw new Error( + `RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.`, + ); + } + + public toString(): string { + return 'RRNode'; + } +} + +export function BaseRRDocumentImpl< + RRNode extends ConstrainedConstructor +>(RRNodeClass: RRNode) { + return class BaseRRDocument extends RRNodeClass implements IRRDocument { + public readonly nodeType: number = NodeType.DOCUMENT_NODE; + public readonly nodeName: '#document' = '#document'; + public readonly compatMode: 'BackCompat' | 'CSS1Compat' = 'CSS1Compat'; + public readonly RRNodeType = RRNodeType.Document; + public textContent: string | null = null; + + public get documentElement(): IRRElement | null { + return ( + (this.childNodes.find( + (node) => + node.RRNodeType === RRNodeType.Element && + (node as IRRElement).tagName === 'HTML', + ) as IRRElement) || null + ); + } + + public get body(): IRRElement | null { + return ( + (this.documentElement?.childNodes.find( + (node) => + node.RRNodeType === RRNodeType.Element && + (node as IRRElement).tagName === 'BODY', + ) as IRRElement) || null + ); + } + + public get head(): IRRElement | null { + return ( + (this.documentElement?.childNodes.find( + (node) => + node.RRNodeType === RRNodeType.Element && + (node as IRRElement).tagName === 'HEAD', + ) as IRRElement) || null + ); + } + + public get implementation(): IRRDocument { + return this; + } + + public get firstElementChild(): IRRElement | null { + return this.documentElement; + } + + public appendChild(childNode: IRRNode): IRRNode { + const nodeType = childNode.RRNodeType; + if ( + nodeType === RRNodeType.Element || + nodeType === RRNodeType.DocumentType + ) { + if (this.childNodes.some((s) => s.RRNodeType === nodeType)) { + throw new Error( + `RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one ${ + nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype' + } on RRDocument allowed.`, + ); + } + } + childNode.parentElement = null; + childNode.parentNode = this; + this.childNodes.push(childNode); + return childNode; + } + + public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode { + const nodeType = newChild.RRNodeType; + if ( + nodeType === RRNodeType.Element || + nodeType === RRNodeType.DocumentType + ) { + if (this.childNodes.some((s) => s.RRNodeType === nodeType)) { + throw new Error( + `RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one ${ + nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype' + } on RRDocument allowed.`, + ); + } + } + if (refChild === null) return this.appendChild(newChild); + const childIndex = this.childNodes.indexOf(refChild); + if (childIndex == -1) + throw new Error( + "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", + ); + this.childNodes.splice(childIndex, 0, newChild); + newChild.parentElement = null; + newChild.parentNode = this; + return newChild; + } + + public removeChild(node: IRRNode) { + const indexOfChild = this.childNodes.indexOf(node); + if (indexOfChild === -1) + throw new Error( + "Failed to execute 'removeChild' on 'RRDocument': The RRNode to be removed is not a child of this RRNode.", + ); + this.childNodes.splice(indexOfChild, 1); + node.parentElement = null; + node.parentNode = null; + return node; + } + + public open() { + this.childNodes = []; + } + + public close() {} + + /** + * Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot). + * There are two lines used this function: + * 1. doc.write('') + * 2. doc.write('') + */ + public write(content: string) { + let publicId; + if ( + content === + '' + ) + publicId = '-//W3C//DTD XHTML 1.0 Transitional//EN'; + else if ( + content === + '' + ) + publicId = '-//W3C//DTD HTML 4.0 Transitional//EN'; + if (publicId) { + const doctype = this.createDocumentType('html', publicId, ''); + this.open(); + this.appendChild(doctype); + } + } + + createDocument( + _namespace: string | null, + _qualifiedName: string | null, + _doctype?: DocumentType | null, + ): IRRDocument { + return new BaseRRDocument(); + } + + createDocumentType( + qualifiedName: string, + publicId: string, + systemId: string, + ): IRRDocumentType { + const doctype = new (BaseRRDocumentTypeImpl(BaseRRNode))( + qualifiedName, + publicId, + systemId, + ); + doctype.ownerDocument = this; + return doctype; + } + + createElement(tagName: string): IRRElement { + const element = new (BaseRRElementImpl(BaseRRNode))(tagName); + element.ownerDocument = this; + return element; + } + + createElementNS(_namespaceURI: string, qualifiedName: string): IRRElement { + return this.createElement(qualifiedName); + } + + createTextNode(data: string): IRRText { + const text = new (BaseRRTextImpl(BaseRRNode))(data); + text.ownerDocument = this; + return text; + } + + createComment(data: string): IRRComment { + const comment = new (BaseRRCommentImpl(BaseRRNode))(data); + comment.ownerDocument = this; + return comment; + } + + createCDATASection(data: string): IRRCDATASection { + const CDATASection = new (BaseRRCDATASectionImpl(BaseRRNode))(data); + CDATASection.ownerDocument = this; + return CDATASection; + } + + toString() { + return 'RRDocument'; + } + }; +} + +export function BaseRRDocumentTypeImpl< + RRNode extends ConstrainedConstructor +>(RRNodeClass: RRNode) { + // @ts-ignore + return class BaseRRDocumentType + extends RRNodeClass + implements IRRDocumentType { + public readonly nodeType: number = NodeType.DOCUMENT_TYPE_NODE; + public readonly RRNodeType = RRNodeType.DocumentType; + public readonly nodeName: string; + public readonly name: string; + public readonly publicId: string; + public readonly systemId: string; + public textContent: string | null = null; + + constructor(qualifiedName: string, publicId: string, systemId: string) { + super(); + this.name = qualifiedName; + this.publicId = publicId; + this.systemId = systemId; + this.nodeName = qualifiedName; + } + + toString() { + return 'RRDocumentType'; + } + }; +} + +export function BaseRRElementImpl< + RRNode extends ConstrainedConstructor +>(RRNodeClass: RRNode) { + // @ts-ignore + return class BaseRRElement extends RRNodeClass implements IRRElement { + public readonly nodeType: number = NodeType.ELEMENT_NODE; + public readonly RRNodeType = RRNodeType.Element; + public readonly nodeName: string; + public tagName: string; + public attributes: Record = {}; + public shadowRoot: IRRElement | null = null; + public scrollLeft?: number; + public scrollTop?: number; + + constructor(tagName: string) { + super(); + this.tagName = tagName.toUpperCase(); + this.nodeName = tagName.toUpperCase(); + } + + public get textContent(): string { + let result = ''; + this.childNodes.forEach((node) => (result += node.textContent)); + return result; + } + + public set textContent(textContent: string) { + this.childNodes = [this.ownerDocument.createTextNode(textContent)]; + } + + public get classList(): ClassList { + return new ClassList( + this.attributes.class as string | undefined, + (newClassName) => { + this.attributes.class = newClassName; + }, + ); + } + + public get id() { + return this.attributes.id || ''; + } + + public get className() { + return this.attributes.class || ''; + } + + public get style() { + const style = (this.attributes.style + ? parseCSSText(this.attributes.style as string) + : {}) as CSSStyleDeclaration; + const hyphenateRE = /\B([A-Z])/g; + style.setProperty = ( + name: string, + value: string | null, + priority?: string, + ) => { + if (hyphenateRE.test(name)) return; + const normalizedName = camelize(name); + if (!value) delete style[normalizedName]; + else style[normalizedName] = value; + if (priority === 'important') style[normalizedName] += ' !important'; + this.attributes.style = toCSSText(style); + }; + style.removeProperty = (name: string) => { + if (hyphenateRE.test(name)) return ''; + const normalizedName = camelize(name); + const value = style[normalizedName] || ''; + delete style[normalizedName]; + this.attributes.style = toCSSText(style); + return value; + }; + return style; + } + + public getAttribute(name: string) { + return this.attributes[name] || null; + } + + public setAttribute(name: string, attribute: string) { + this.attributes[name] = attribute; + } + + public setAttributeNS( + _namespace: string | null, + qualifiedName: string, + value: string, + ): void { + this.setAttribute(qualifiedName, value); + } + + public removeAttribute(name: string) { + delete this.attributes[name]; + } + + public appendChild(newChild: IRRNode): IRRNode { + this.childNodes.push(newChild); + newChild.parentNode = this; + newChild.parentElement = this; + return newChild; + } + + public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode { + if (refChild === null) return this.appendChild(newChild); + const childIndex = this.childNodes.indexOf(refChild); + if (childIndex == -1) + throw new Error( + "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", + ); + this.childNodes.splice(childIndex, 0, newChild); + newChild.parentElement = this; + newChild.parentNode = this; + return newChild; + } + + public removeChild(node: IRRNode): IRRNode { + const indexOfChild = this.childNodes.indexOf(node); + if (indexOfChild === -1) + throw new Error( + "Failed to execute 'removeChild' on 'RRElement': The RRNode to be removed is not a child of this RRNode.", + ); + this.childNodes.splice(indexOfChild, 1); + node.parentElement = null; + node.parentNode = null; + return node; + } + + public attachShadow(_init: ShadowRootInit): IRRElement { + const shadowRoot = this.ownerDocument.createElement('SHADOWROOT'); + this.shadowRoot = shadowRoot; + return shadowRoot; + } + + public dispatchEvent(_event: Event) { + return true; + } + + toString() { + let attributeString = ''; + for (let attribute in this.attributes) { + attributeString += `${attribute}="${this.attributes[attribute]}" `; + } + return `${this.tagName} ${attributeString}`; + } + }; +} + +export function BaseRRMediaElementImpl< + RRElement extends ConstrainedConstructor +>(RRElementClass: RRElement) { + return class BaseRRMediaElement extends RRElementClass { + public currentTime?: number; + public volume?: number; + public paused?: boolean; + public muted?: boolean; + attachShadow(_init: ShadowRootInit): IRRElement { + throw new Error( + `RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`, + ); + } + public play() { + this.paused = false; + } + public pause() { + this.paused = true; + } + }; +} + +export function BaseRRTextImpl>( + RRNodeClass: RRNode, +) { + // @ts-ignore + return class BaseRRText extends RRNodeClass implements IRRText { + public readonly nodeType: number = NodeType.TEXT_NODE; + public readonly nodeName: '#text' = '#text'; + public readonly RRNodeType = RRNodeType.Text; + public data: string; + + constructor(data: string) { + super(); + this.data = data; + } + + public get textContent(): string { + return this.data; + } + + public set textContent(textContent: string) { + this.data = textContent; + } + + toString() { + return `RRText text=${JSON.stringify(this.data)}`; + } + }; +} + +export function BaseRRCommentImpl< + RRNode extends ConstrainedConstructor +>(RRNodeClass: RRNode) { + // @ts-ignore + return class BaseRRComment extends RRNodeClass implements IRRComment { + public readonly nodeType: number = NodeType.COMMENT_NODE; + public readonly nodeName: '#comment' = '#comment'; + public readonly RRNodeType = RRNodeType.Comment; + public data: string; + + constructor(data: string) { + super(); + this.data = data; + } + + public get textContent(): string { + return this.data; + } + + public set textContent(textContent: string) { + this.data = textContent; + } + + toString() { + return `RRComment text=${JSON.stringify(this.data)}`; + } + }; +} + +export function BaseRRCDATASectionImpl< + RRNode extends ConstrainedConstructor +>(RRNodeClass: RRNode) { + // @ts-ignore + return class BaseRRCDATASection + extends RRNodeClass + implements IRRCDATASection { + public readonly nodeName: '#cdata-section' = '#cdata-section'; + public readonly nodeType: number = NodeType.CDATA_SECTION_NODE; + public readonly RRNodeType = RRNodeType.CDATA; + public data: string; + + constructor(data: string) { + super(); + this.data = data; + } + + public get textContent(): string { + return this.data; + } + + public set textContent(textContent: string) { + this.data = textContent; + } + + toString() { + return `RRCDATASection data=${JSON.stringify(this.data)}`; + } + }; +} + +export class ClassList { + private onChange: ((newClassText: string) => void) | undefined; + classes: string[] = []; + + constructor( + classText?: string, + onChange?: ((newClassText: string) => void) | undefined, + ) { + if (classText) { + const classes = classText.trim().split(/\s+/); + this.classes.push(...classes); + } + this.onChange = onChange; + } + + add = (...classNames: string[]) => { + for (const item of classNames) { + const className = String(item); + if (this.classes.indexOf(className) >= 0) continue; + this.classes.push(className); + } + this.onChange && this.onChange(this.classes.join(' ')); + }; + + remove = (...classNames: string[]) => { + this.classes = this.classes.filter( + (item) => classNames.indexOf(item) === -1, + ); + this.onChange && this.onChange(this.classes.join(' ')); + }; +} + +export type CSSStyleDeclaration = Record & { + setProperty: ( + name: string, + value: string | null, + priority?: string | null, + ) => void; + removeProperty: (name: string) => string; +}; + +// Enumerate nodeType value of standard HTML Node. +export enum NodeType { + PLACEHOLDER, // This isn't a node type. Enum type value starts from zero but NodeType value starts from 1. + ELEMENT_NODE, + ATTRIBUTE_NODE, + TEXT_NODE, + CDATA_SECTION_NODE, + ENTITY_REFERENCE_NODE, + ENTITY_NODE, + PROCESSING_INSTRUCTION_NODE, + COMMENT_NODE, + DOCUMENT_NODE, + DOCUMENT_TYPE_NODE, + DOCUMENT_FRAGMENT_NODE, +} diff --git a/packages/rrdom/src/polyfill.ts b/packages/rrdom/src/polyfill.ts index 271af35f56..b93c556542 100644 --- a/packages/rrdom/src/polyfill.ts +++ b/packages/rrdom/src/polyfill.ts @@ -2,6 +2,8 @@ import { RRDocument, RRNode } from './document-nodejs'; /** * Polyfill the performance for nodejs. + * Note: The performance api is available through the global object from nodejs v16.0.0. + * https://github.com/nodejs/node/pull/37970 */ export function polyfillPerformance() { if (typeof window !== 'undefined' || 'performance' in global) return; @@ -80,8 +82,8 @@ export function polyfillDocument() { const rrdom = new RRDocument(); (() => { rrdom.appendChild(rrdom.createElement('html')); - rrdom.documentElement.appendChild(rrdom.createElement('head')); - rrdom.documentElement.appendChild(rrdom.createElement('body')); + rrdom.documentElement!.appendChild(rrdom.createElement('head')); + rrdom.documentElement!.appendChild(rrdom.createElement('body')); })(); global.document = (rrdom as unknown) as Document; } diff --git a/packages/rrdom/src/style.ts b/packages/rrdom/src/style.ts index 1e49b83619..a85f7598f0 100644 --- a/packages/rrdom/src/style.ts +++ b/packages/rrdom/src/style.ts @@ -1,40 +1,45 @@ export function parseCSSText(cssText: string): Record { - const res: Record = {}; - const listDelimiter = /;(?![^(]*\))/g; - const propertyDelimiter = /:(.+)/; - cssText.split(listDelimiter).forEach(function (item) { - if (item) { - const tmp = item.split(propertyDelimiter); - tmp.length > 1 && (res[camelize(tmp[0].trim())] = tmp[1].trim()); - } - }); - return res; + const res: Record = {}; + const listDelimiter = /;(?![^(]*\))/g; + const propertyDelimiter = /:(.+)/; + const comment = /\/\*.*?\*\//g; + cssText + .replace(comment, '') + .split(listDelimiter) + .forEach(function (item) { + if (item) { + const tmp = item.split(propertyDelimiter); + tmp.length > 1 && (res[camelize(tmp[0].trim())] = tmp[1].trim()); + } + }); + return res; +} + +export function toCSSText(style: Record): string { + const properties = []; + for (let name in style) { + const value = style[name]; + if (typeof value !== 'string') continue; + const normalizedName = hyphenate(name); + properties.push(`${normalizedName}: ${value};`); } - - export function toCSSText(style: Record): string { - const properties = []; - for (let name in style) { - const value = style[name]; - if (typeof value !== 'string') continue; - const normalizedName = hyphenate(name); - properties.push(`${normalizedName}:${value};`); - } - return properties.join(' '); - } - - /** - * Camelize a hyphen-delimited string. - */ - const camelizeRE = /-(\w)/g; - export const camelize = (str: string): string => { - return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')); - }; - - /** - * Hyphenate a camelCase string. - */ - const hyphenateRE = /\B([A-Z])/g; - export const hyphenate = (str: string): string => { - return str.replace(hyphenateRE, '-$1').toLowerCase(); - }; - \ No newline at end of file + return properties.join(' '); +} + +/** + * Camelize a hyphen-delimited string. + */ +const camelizeRE = /-([a-z])/g; +const CUSTOM_PROPERTY_REGEX = /^--[a-zA-Z0-9-]+$/; +export const camelize = (str: string): string => { + if (CUSTOM_PROPERTY_REGEX.test(str)) return str; + return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')); +}; + +/** + * Hyphenate a camelCase string. + */ +const hyphenateRE = /\B([A-Z])/g; +export const hyphenate = (str: string): string => { + return str.replace(hyphenateRE, '-$1').toLowerCase(); +}; diff --git a/packages/rrdom/src/virtual-dom.ts b/packages/rrdom/src/virtual-dom.ts new file mode 100644 index 0000000000..86f59f85b6 --- /dev/null +++ b/packages/rrdom/src/virtual-dom.ts @@ -0,0 +1,450 @@ +import { + NodeType as RRNodeType, + createMirror as createNodeMirror, +} from 'rrweb-snapshot'; +import type { + Mirror as NodeMirror, + IMirror, + serializedNodeWithId, +} from 'rrweb-snapshot'; +import type { + canvasMutationData, + canvasEventWithTime, + inputData, + scrollData, +} from 'rrweb/src/types'; +import { + BaseRRNode as RRNode, + BaseRRCDATASectionImpl, + BaseRRCommentImpl, + BaseRRDocumentImpl, + BaseRRDocumentTypeImpl, + BaseRRElementImpl, + BaseRRMediaElementImpl, + BaseRRTextImpl, + IRRDocument, + IRRElement, + IRRNode, + NodeType, + IRRDocumentType, + IRRText, + IRRComment, +} from './document'; +import type { VirtualStyleRules } from './diff'; + +export class RRDocument extends BaseRRDocumentImpl(RRNode) { + // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. + // These unserialized nodes may interfere the execution of the diff algorithm. + // The id of serialized node is larger than 0. So this value ​​less than 0 is used as id for these unserialized nodes. + private _unserializedId = -1; + + /** + * Every time the id is used, it will minus 1 automatically to avoid collisions. + */ + public get unserializedId(): number { + return this._unserializedId--; + } + + public mirror: Mirror = createMirror(); + + public scrollData: scrollData | null = null; + + constructor(mirror?: Mirror) { + super(); + if (mirror) { + this.mirror = mirror; + } + } + + createDocument( + _namespace: string | null, + _qualifiedName: string | null, + _doctype?: DocumentType | null, + ) { + return new RRDocument(); + } + + createDocumentType( + qualifiedName: string, + publicId: string, + systemId: string, + ) { + const documentTypeNode = new RRDocumentType( + qualifiedName, + publicId, + systemId, + ); + documentTypeNode.ownerDocument = this; + return documentTypeNode; + } + + createElement( + tagName: K, + ): RRElementType; + createElement(tagName: string): RRElement; + createElement(tagName: string) { + const upperTagName = tagName.toUpperCase(); + let element; + switch (upperTagName) { + case 'AUDIO': + case 'VIDEO': + element = new RRMediaElement(upperTagName); + break; + case 'IFRAME': + element = new RRIFrameElement(upperTagName, this.mirror); + break; + case 'CANVAS': + element = new RRCanvasElement(upperTagName); + break; + case 'STYLE': + element = new RRStyleElement(upperTagName); + break; + default: + element = new RRElement(upperTagName); + break; + } + element.ownerDocument = this; + return element; + } + + createComment(data: string) { + const commentNode = new RRComment(data); + commentNode.ownerDocument = this; + return commentNode; + } + + createCDATASection(data: string) { + const sectionNode = new RRCDATASection(data); + sectionNode.ownerDocument = this; + return sectionNode; + } + + createTextNode(data: string) { + const textNode = new RRText(data); + textNode.ownerDocument = this; + return textNode; + } + + destroyTree() { + this.childNodes = []; + this.mirror.reset(); + } + + open() { + super.open(); + this._unserializedId = -1; + } +} + +export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); + +export class RRElement extends BaseRRElementImpl(RRNode) { + inputData: inputData | null = null; + scrollData: scrollData | null = null; +} + +export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} + +export class RRCanvasElement extends RRElement implements IRRElement { + public canvasMutations: { + event: canvasEventWithTime; + mutation: canvasMutationData; + }[] = []; + /** + * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. + */ + getContext(): RenderingContext | null { + return null; + } +} + +export class RRStyleElement extends RRElement { + public rules: VirtualStyleRules = []; +} + +export class RRIFrameElement extends RRElement { + contentDocument: RRDocument = new RRDocument(); + constructor(upperTagName: string, mirror: Mirror) { + super(upperTagName); + this.contentDocument.mirror = mirror; + } +} + +export const RRText = BaseRRTextImpl(RRNode); +export type RRText = typeof RRText; + +export const RRComment = BaseRRCommentImpl(RRNode); +export type RRComment = typeof RRComment; + +export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); +export type RRCDATASection = typeof RRCDATASection; + +interface RRElementTagNameMap { + audio: RRMediaElement; + canvas: RRCanvasElement; + iframe: RRIFrameElement; + style: RRStyleElement; + video: RRMediaElement; +} + +type RRElementType< + K extends keyof HTMLElementTagNameMap +> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; + +function getValidTagName(element: HTMLElement): string { + // https://github.com/rrweb-io/rrweb-snapshot/issues/56 + if (element instanceof HTMLFormElement) { + return 'FORM'; + } + return element.tagName.toUpperCase(); +} + +/** + * Build a RRNode from a real Node. + * @param node the real Node + * @param rrdom the RRDocument + * @param domMirror the NodeMirror that records the real document tree + * @returns the built RRNode + */ +export function buildFromNode( + node: Node, + rrdom: IRRDocument, + domMirror: NodeMirror, + parentRRNode?: IRRNode | null, +): IRRNode | null { + let rrNode: IRRNode; + + switch (node.nodeType) { + case NodeType.DOCUMENT_NODE: + if (parentRRNode && parentRRNode.nodeName === 'IFRAME') + rrNode = (parentRRNode as RRIFrameElement).contentDocument; + else { + rrNode = rrdom; + (rrNode as IRRDocument).compatMode = (node as Document).compatMode as + | 'BackCompat' + | 'CSS1Compat'; + } + break; + case NodeType.DOCUMENT_TYPE_NODE: + const documentType = (node as Node) as DocumentType; + rrNode = rrdom.createDocumentType( + documentType.name, + documentType.publicId, + documentType.systemId, + ); + break; + case NodeType.ELEMENT_NODE: + const elementNode = (node as Node) as HTMLElement; + const tagName = getValidTagName(elementNode); + rrNode = rrdom.createElement(tagName); + const rrElement = rrNode as IRRElement; + for (const { name, value } of Array.from(elementNode.attributes)) { + rrElement.attributes[name] = value; + } + elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); + elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); + /** + * We don't have to record special values of input elements at the beginning. + * Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed. + */ + break; + case NodeType.TEXT_NODE: + rrNode = rrdom.createTextNode(((node as Node) as Text).textContent || ''); + break; + case NodeType.CDATA_SECTION_NODE: + rrNode = rrdom.createCDATASection(((node as Node) as CDATASection).data); + break; + case NodeType.COMMENT_NODE: + rrNode = rrdom.createComment( + ((node as Node) as Comment).textContent || '', + ); + break; + // if node is a shadow root + case NodeType.DOCUMENT_FRAGMENT_NODE: + rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' }); + break; + default: + return null; + } + + let sn: serializedNodeWithId | null = domMirror.getMeta(node); + + if (rrdom instanceof RRDocument) { + if (!sn) { + sn = getDefaultSN(rrNode, rrdom.unserializedId); + domMirror.add(node, sn); + } + rrdom.mirror.add(rrNode, { ...sn }); + } + + return rrNode; +} + +/** + * Build a RRDocument from a real document tree. + * @param dom the real document tree + * @param domMirror the NodeMirror that records the real document tree + * @param rrdom the rrdom object to be constructed + * @returns the build rrdom + */ +export function buildFromDom( + dom: Document, + domMirror: NodeMirror = createNodeMirror(), + rrdom: IRRDocument = new RRDocument(), +) { + function walk(node: Node, parentRRNode: IRRNode | null) { + const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); + if (rrNode === null) return; + if ( + // if the parentRRNode isn't a RRIFrameElement + parentRRNode?.nodeName !== 'IFRAME' && + // if node isn't a shadow root + node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE + ) { + parentRRNode?.appendChild(rrNode); + rrNode.parentNode = parentRRNode; + rrNode.parentElement = parentRRNode as RRElement; + } + + if (node.nodeName === 'IFRAME') { + walk((node as HTMLIFrameElement).contentDocument!, rrNode); + } else if ( + node.nodeType === NodeType.DOCUMENT_NODE || + node.nodeType === NodeType.ELEMENT_NODE || + node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE + ) { + // if the node is a shadow dom + if ( + node.nodeType === NodeType.ELEMENT_NODE && + ((node as Node) as HTMLElement).shadowRoot + ) + walk(((node as Node) as HTMLElement).shadowRoot!, rrNode); + node.childNodes.forEach((childNode) => walk(childNode, rrNode)); + } + } + walk(dom, null); + return rrdom; +} + +export function createMirror(): Mirror { + return new Mirror(); +} + +// based on Mirror from rrweb-snapshots +export class Mirror implements IMirror { + private idNodeMap: Map = new Map(); + private nodeMetaMap: WeakMap = new WeakMap(); + + getId(n: RRNode | undefined | null): number { + if (!n) return -1; + + const id = this.getMeta(n)?.id; + + // if n is not a serialized Node, use -1 as its id. + return id ?? -1; + } + + getNode(id: number): RRNode | null { + return this.idNodeMap.get(id) || null; + } + + getIds(): number[] { + return Array.from(this.idNodeMap.keys()); + } + + getMeta(n: RRNode): serializedNodeWithId | null { + return this.nodeMetaMap.get(n) || null; + } + + // removes the node from idNodeMap + // doesn't remove the node from nodeMetaMap + removeNodeFromMap(n: RRNode) { + const id = this.getId(n); + this.idNodeMap.delete(id); + + if (n.childNodes) { + n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); + } + } + has(id: number): boolean { + return this.idNodeMap.has(id); + } + + hasNode(node: RRNode): boolean { + return this.nodeMetaMap.has(node); + } + + add(n: RRNode, meta: serializedNodeWithId) { + const id = meta.id; + this.idNodeMap.set(id, n); + this.nodeMetaMap.set(n, meta); + } + + replace(id: number, n: RRNode) { + this.idNodeMap.set(id, n); + } + + reset() { + this.idNodeMap = new Map(); + this.nodeMetaMap = new WeakMap(); + } +} + +/** + * Get a default serializedNodeWithId value for a RRNode. + * @param id the serialized id to assign + */ +export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { + switch (node.RRNodeType) { + case RRNodeType.Document: + return { + id, + type: node.RRNodeType, + childNodes: [], + }; + case RRNodeType.DocumentType: + const doctype = node as IRRDocumentType; + return { + id, + type: node.RRNodeType, + name: doctype.name, + publicId: doctype.publicId, + systemId: doctype.systemId, + }; + case RRNodeType.Element: + return { + id, + type: node.RRNodeType, + tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. + attributes: {}, + childNodes: [], + }; + case RRNodeType.Text: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRText).textContent || '', + }; + case RRNodeType.Comment: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRComment).textContent || '', + }; + case RRNodeType.CDATA: + return { + id, + type: node.RRNodeType, + textContent: '', + }; + } +} + +export { RRNode }; +export { + diff, + createOrGetNode, + StyleRuleType, + VirtualStyleRules, + ReplayerHandler, +} from './diff'; diff --git a/packages/rrdom/test/__snapshots__/document-nodejs.test.ts.snap b/packages/rrdom/test/__snapshots__/document-nodejs.test.ts.snap deleted file mode 100644 index 04748dd7a8..0000000000 --- a/packages/rrdom/test/__snapshots__/document-nodejs.test.ts.snap +++ /dev/null @@ -1,48 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`RRDocument for nodejs environment buildFromDom should create an RRDocument from a html document 1`] = ` -"-1 RRDocument - -2 RRDocumentType - -3 HTML lang=\\"en\\" - -4 HEAD - -5 RRText text=\\"\\\\n \\" - -6 META charset=\\"UTF-8\\" - -7 RRText text=\\"\\\\n \\" - -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" - -9 RRText text=\\"\\\\n \\" - -10 TITLE - -11 RRText text=\\"Main\\" - -12 RRText text=\\"\\\\n \\" - -13 LINK rel=\\"stylesheet\\" href=\\"somelink\\" - -14 RRText text=\\"\\\\n \\" - -15 STYLE - -16 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url(\\\\\\"main.css\\\\\\");\\\\n \\" - -17 RRText text=\\"\\\\n \\" - -18 RRText text=\\"\\\\n \\" - -19 BODY - -20 RRText text=\\"\\\\n \\" - -21 H1 - -22 RRText text=\\"This is a h1 heading\\" - -23 RRText text=\\"\\\\n \\" - -24 H1 style=\\"font-size: 16px\\" - -25 RRText text=\\"This is a h1 heading with styles\\" - -26 RRText text=\\"\\\\n \\" - -27 DIV id=\\"block1\\" class=\\"blocks blocks1\\" - -28 RRText text=\\"\\\\n \\" - -29 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\" - -30 RRText text=\\"\\\\n Text 1\\\\n \\" - -31 DIV id=\\"block3\\" - -32 RRText text=\\"\\\\n \\" - -33 P - -34 RRText text=\\"This is a paragraph\\" - -35 RRText text=\\"\\\\n \\" - -36 BUTTON - -37 RRText text=\\"button1\\" - -38 RRText text=\\"\\\\n \\" - -39 RRText text=\\"\\\\n Text 2\\\\n \\" - -40 RRText text=\\"\\\\n \\" - -41 IMG src=\\"somelink\\" alt=\\"This is an image\\" - -42 RRText text=\\"\\\\n \\" - -43 RRText text=\\"\\\\n \\\\n\\\\n\\" -" -`; diff --git a/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap b/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap new file mode 100644 index 0000000000..c1dc00248b --- /dev/null +++ b/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap @@ -0,0 +1,160 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RRDocument for browser environment create a RRDocument from a html document can build from a common html 1`] = ` +"-1 RRDocument + -2 RRDocumentType + -3 HTML lang=\\"en\\" + -4 HEAD + -5 RRText text=\\"\\\\n \\" + -6 META charset=\\"UTF-8\\" + -7 RRText text=\\"\\\\n \\" + -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -9 RRText text=\\"\\\\n \\" + -10 TITLE + -11 RRText text=\\"Main\\" + -12 RRText text=\\"\\\\n \\" + -13 LINK rel=\\"stylesheet\\" href=\\"somelink\\" + -14 RRText text=\\"\\\\n \\" + -15 STYLE + -16 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url('main.css');\\\\n \\" + -17 RRText text=\\"\\\\n \\" + -18 RRText text=\\"\\\\n \\" + -19 BODY + -20 RRText text=\\"\\\\n \\" + -21 H1 + -22 RRText text=\\"This is a h1 heading\\" + -23 RRText text=\\"\\\\n \\" + -24 H1 style=\\"font-size: 16px\\" + -25 RRText text=\\"This is a h1 heading with styles\\" + -26 RRText text=\\"\\\\n \\" + -27 DIV id=\\"block1\\" class=\\"blocks blocks1\\" + -28 RRText text=\\"\\\\n \\" + -29 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\" + -30 RRText text=\\"\\\\n Text 1\\\\n \\" + -31 DIV id=\\"block3\\" + -32 RRText text=\\"\\\\n \\" + -33 P + -34 RRText text=\\"This is a paragraph\\" + -35 RRText text=\\"\\\\n \\" + -36 BUTTON + -37 RRText text=\\"button1\\" + -38 RRText text=\\"\\\\n \\" + -39 RRText text=\\"\\\\n Text 2\\\\n \\" + -40 RRText text=\\"\\\\n \\" + -41 IMG src=\\"somelink\\" alt=\\"This is an image\\" + -42 RRText text=\\"\\\\n \\" + -43 RRComment text=\\" This is a line of comment \\" + -44 RRText text=\\"\\\\n \\" + -45 FORM + -46 RRText text=\\"\\\\n \\" + -47 INPUT type=\\"text\\" id=\\"input1\\" + -48 RRText text=\\"\\\\n \\" + -49 RRText text=\\"\\\\n \\" + -50 RRText text=\\"\\\\n \\\\n\\\\n\\" +" +`; + +exports[`RRDocument for browser environment create a RRDocument from a html document can build from a html containing nested shadow doms 1`] = ` +"-1 RRDocument + -2 RRDocumentType + -3 HTML lang=\\"en\\" + -4 HEAD + -5 RRText text=\\"\\\\n \\" + -6 META charset=\\"UTF-8\\" + -7 RRText text=\\"\\\\n \\" + -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -9 RRText text=\\"\\\\n \\" + -10 TITLE + -11 RRText text=\\"shadow dom\\" + -12 RRText text=\\"\\\\n \\" + -13 RRText text=\\"\\\\n \\" + -14 BODY + -15 RRText text=\\"\\\\n \\" + -16 DIV + -17 SHADOWROOT + -18 RRText text=\\"\\\\n \\" + -19 SPAN + -20 RRText text=\\" shadow dom one \\" + -21 RRText text=\\"\\\\n \\" + -22 DIV + -23 SHADOWROOT + -24 RRText text=\\"\\\\n \\" + -25 SPAN + -26 RRText text=\\" shadow dom two \\" + -27 RRText text=\\"\\\\n \\" + -28 RRText text=\\"\\\\n \\\\n \\" + -29 RRText text=\\"\\\\n \\" + -30 RRText text=\\"\\\\n \\\\n \\" + -31 RRText text=\\"\\\\n \\\\n\\\\n\\" +" +`; + +exports[`RRDocument for browser environment create a RRDocument from a html document can build from a xml page 1`] = ` +"-1 RRDocument + -2 XML + -3 RRCDATASection data=\\"Some data & then some\\" +" +`; + +exports[`RRDocument for browser environment create a RRDocument from a html document can build from an iframe html 1`] = ` +"-1 RRDocument + -2 RRDocumentType + -3 HTML lang=\\"en\\" + -4 HEAD + -5 RRText text=\\"\\\\n \\" + -6 META charset=\\"UTF-8\\" + -7 RRText text=\\"\\\\n \\" + -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -9 RRText text=\\"\\\\n \\" + -10 TITLE + -11 RRText text=\\"Iframe\\" + -12 RRText text=\\"\\\\n \\" + -13 RRText text=\\"\\\\n \\" + -14 BODY + -15 RRText text=\\"\\\\n \\" + -16 IFRAME id=\\"iframe1\\" srcdoc=\\" + + + + + + +
This is a block inside the iframe1.
+ + + + diff --git a/packages/rrdom/test/html/main.html b/packages/rrdom/test/html/main.html index c9603b3066..40e3c169e2 100644 --- a/packages/rrdom/test/html/main.html +++ b/packages/rrdom/test/html/main.html @@ -4,7 +4,7 @@ Main - + @@ -35,6 +35,10 @@

This is a h1 heading with styles

Text 2 This is an image + +
+ +
diff --git a/packages/rrdom/test/html/shadow-dom.html b/packages/rrdom/test/html/shadow-dom.html new file mode 100644 index 0000000000..0704dc0430 --- /dev/null +++ b/packages/rrdom/test/html/shadow-dom.html @@ -0,0 +1,20 @@ + + + + + + shadow dom + + +
+ +
+ + diff --git a/packages/rrdom/test/polyfill.test.ts b/packages/rrdom/test/polyfill.test.ts index 6222f7de3f..a9d5f381f1 100644 --- a/packages/rrdom/test/polyfill.test.ts +++ b/packages/rrdom/test/polyfill.test.ts @@ -1,3 +1,4 @@ +import { compare } from 'compare-versions'; import { RRDocument, RRNode } from '../src/document-nodejs'; import { polyfillPerformance, @@ -9,7 +10,8 @@ import { describe('polyfill for nodejs', () => { it('should polyfill performance api', () => { - expect(global.performance).toBeUndefined(); + if (compare(process.version, 'v16.0.0', '<')) + expect(global.performance).toBeUndefined(); polyfillPerformance(); expect(global.performance).toBeDefined(); expect(performance).toBeDefined(); @@ -20,6 +22,18 @@ describe('polyfill for nodejs', () => { ); }); + it('should not polyfill performance if it already exists', () => { + if (compare(process.version, 'v16.0.0', '>=')) { + const originalPerformance = global.performance; + polyfillPerformance(); + expect(global.performance).toBe(originalPerformance); + } + const fakePerformance = (jest.fn() as unknown) as Performance; + global.performance = fakePerformance; + polyfillPerformance(); + expect(global.performance).toEqual(fakePerformance); + }); + it('should polyfill requestAnimationFrame', () => { expect(global.requestAnimationFrame).toBeUndefined(); expect(global.cancelAnimationFrame).toBeUndefined(); @@ -59,12 +73,32 @@ describe('polyfill for nodejs', () => { jest.useRealTimers(); }); + it('should not polyfill requestAnimationFrame if it already exists', () => { + const fakeRequestAnimationFrame = (jest.fn() as unknown) as typeof global.requestAnimationFrame; + global.requestAnimationFrame = fakeRequestAnimationFrame; + const fakeCancelAnimationFrame = (jest.fn() as unknown) as typeof global.cancelAnimationFrame; + global.cancelAnimationFrame = fakeCancelAnimationFrame; + polyfillRAF(); + expect(global.requestAnimationFrame).toBe(fakeRequestAnimationFrame); + expect(global.cancelAnimationFrame).toBe(fakeCancelAnimationFrame); + }); + it('should polyfill Event type', () => { + // if the second version is greater + if (compare(process.version, 'v15.0.0', '<')) + expect(global.Event).toBeUndefined(); polyfillEvent(); expect(global.Event).toBeDefined(); expect(Event).toBeDefined(); }); + it('should not polyfill Event type if it already exists', () => { + const fakeEvent = (jest.fn() as unknown) as typeof global.Event; + global.Event = fakeEvent; + polyfillEvent(); + expect(global.Event).toBe(fakeEvent); + }); + it('should polyfill Node type', () => { expect(global.Node).toBeUndefined(); polyfillNode(); @@ -73,6 +107,13 @@ describe('polyfill for nodejs', () => { expect(Node).toEqual(RRNode); }); + it('should not polyfill Node type if it already exists', () => { + const fakeNode = (jest.fn() as unknown) as typeof global.Node; + global.Node = fakeNode; + polyfillNode(); + expect(global.Node).toBe(fakeNode); + }); + it('should polyfill document object', () => { expect(global.document).toBeUndefined(); polyfillDocument(); @@ -80,4 +121,11 @@ describe('polyfill for nodejs', () => { expect(document).toBeDefined(); expect(document).toBeInstanceOf(RRDocument); }); + + it('should not polyfill document object if it already exists', () => { + const fakeDocument = (jest.fn() as unknown) as typeof global.document; + global.document = fakeDocument; + polyfillDocument(); + expect(global.document).toBe(fakeDocument); + }); }); diff --git a/packages/rrdom/test/util.ts b/packages/rrdom/test/util.ts deleted file mode 100644 index 09b860fee0..0000000000 --- a/packages/rrdom/test/util.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { RRIframeElement, RRNode } from '../src/document-nodejs'; - -/** - * Print the RRDom as a string. - * @param rootNode the root node of the RRDom tree - * @returns printed string - */ -export function printRRDom(rootNode: RRNode) { - return walk(rootNode, ''); -} - -function walk(node: RRNode, blankSpace: string) { - let printText = `${blankSpace}${node.toString()}\n`; - for (const child of node.childNodes) - printText += walk(child, blankSpace + ' '); - if (node instanceof RRIframeElement) - printText += walk(node.contentDocument, blankSpace + ' '); - return printText; -} diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts new file mode 100644 index 0000000000..4bded073b2 --- /dev/null +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -0,0 +1,550 @@ +/** + * @jest-environment jsdom + */ +import * as fs from 'fs'; +import * as path from 'path'; +import * as puppeteer from 'puppeteer'; +import * as rollup from 'rollup'; +import resolve from '@rollup/plugin-node-resolve'; +import * as typescript from 'rollup-plugin-typescript2'; +import { JSDOM } from 'jsdom'; +import { + cdataNode, + commentNode, + documentNode, + documentTypeNode, + elementNode, + Mirror, + NodeType, + NodeType as RRNodeType, + textNode, +} from 'rrweb-snapshot'; +import { + buildFromDom, + buildFromNode, + createMirror, + getDefaultSN, + RRCanvasElement, + RRDocument, + RRElement, + RRNode, +} from '../src/virtual-dom'; + +const _typescript = (typescript as unknown) as typeof typescript.default; +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(() => { + mirror = new Mirror(); + }); + + describe('create a RRNode from a real Node', () => { + it('should support quicksmode documents', () => { + // seperate jsdom document as changes to the document would otherwise bleed into other tests + const dom = new JSDOM(); + const document = dom.window.document; + + expect(document.doctype).toBeNull(); // confirm compatMode is 'BackCompat' in JSDOM + + const rrdom = new RRDocument(); + let rrNode = buildFromNode(document, rrdom, mirror)!; + + expect((rrNode as RRDocument).compatMode).toBe('BackCompat'); + }); + + it('can patch serialized ID for an unserialized node', () => { + // build from document + expect(mirror.getMeta(document)).toBeNull(); + const rrdom = new RRDocument(); + let rrNode = buildFromNode(document, rrdom, mirror)!; + expect(mirror.getMeta(document)).toBeDefined(); + expect(mirror.getId(document)).toEqual(-1); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document); + expect(rrdom.mirror.getId(rrNode)).toEqual(-1); + expect(rrNode).toBe(rrdom); + + // build from document type + expect(mirror.getMeta(document.doctype!)).toBeNull(); + rrNode = buildFromNode(document.doctype!, rrdom, mirror)!; + expect(mirror.getMeta(document.doctype!)).toBeDefined(); + expect(mirror.getId(document.doctype)).toEqual(-2); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual( + RRNodeType.DocumentType, + ); + expect(rrdom.mirror.getId(rrNode)).toEqual(-2); + + // build from element + expect(mirror.getMeta(document.documentElement)).toBeNull(); + rrNode = buildFromNode( + (document.documentElement as unknown) as Node, + rrdom, + mirror, + )!; + expect(mirror.getMeta(document.documentElement)).toBeDefined(); + expect(mirror.getId(document.documentElement)).toEqual(-3); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Element); + expect(rrdom.mirror.getId(rrNode)).toEqual(-3); + + // build from text + const text = document.createTextNode('text'); + expect(mirror.getMeta(text)).toBeNull(); + rrNode = buildFromNode(text, rrdom, mirror)!; + expect(mirror.getMeta(text)).toBeDefined(); + expect(mirror.getId(text)).toEqual(-4); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Text); + expect(rrdom.mirror.getId(rrNode)).toEqual(-4); + + // build from comment + const comment = document.createComment('comment'); + expect(mirror.getMeta(comment)).toBeNull(); + rrNode = buildFromNode(comment, rrdom, mirror)!; + expect(mirror.getMeta(comment)).toBeDefined(); + expect(mirror.getId(comment)).toEqual(-5); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Comment); + expect(rrdom.mirror.getId(rrNode)).toEqual(-5); + + // build from CDATASection + const xmlDoc = new DOMParser().parseFromString( + '', + 'application/xml', + ); + const cdata = 'Some data & then some'; + var cdataSection = xmlDoc.createCDATASection(cdata); + expect(mirror.getMeta(cdataSection)).toBeNull(); + expect(mirror.getMeta(cdataSection)).toBeNull(); + rrNode = buildFromNode(cdataSection, rrdom, mirror)!; + expect(mirror.getMeta(cdataSection)).toBeDefined(); + expect(mirror.getId(cdataSection)).toEqual(-6); + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.CDATA); + expect(rrdom.mirror.getId(rrNode)).toEqual(-6); + expect(rrNode.textContent).toEqual(cdata); + }); + + it('can record scroll position from HTMLElements', () => { + expect(document.body.scrollLeft).toEqual(0); + expect(document.body.scrollTop).toEqual(0); + const rrdom = new RRDocument(); + let rrNode = buildFromNode(document.body, rrdom, mirror)!; + expect((rrNode as RRElement).scrollLeft).toBeUndefined(); + expect((rrNode as RRElement).scrollTop).toBeUndefined(); + + document.body.scrollLeft = 100; + document.body.scrollTop = 200; + expect(document.body.scrollLeft).toEqual(100); + expect(document.body.scrollTop).toEqual(200); + rrNode = buildFromNode(document.body, rrdom, mirror)!; + expect((rrNode as RRElement).scrollLeft).toEqual(100); + expect((rrNode as RRElement).scrollTop).toEqual(200); + }); + + it('can build contentDocument from an iframe element', () => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + expect(iframe.contentDocument).not.toBeNull(); + const rrdom = new RRDocument(); + const RRIFrame = rrdom.createElement('iframe'); + const rrNode = buildFromNode( + iframe.contentDocument!, + rrdom, + mirror, + RRIFrame, + )!; + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document); + expect(rrdom.mirror.getId(rrNode)).toEqual(-1); + expect(mirror.getId(iframe.contentDocument)).toEqual(-1); + expect(rrNode).toBe(RRIFrame.contentDocument); + }); + + it('can build from a shadow dom', () => { + const div = document.createElement('div'); + div.attachShadow({ mode: 'open' }); + expect(div.shadowRoot).toBeDefined(); + const rrdom = new RRDocument(); + const parentRRNode = rrdom.createElement('div'); + const rrNode = buildFromNode( + div.shadowRoot!, + rrdom, + mirror, + parentRRNode, + )!; + expect(rrNode).not.toBeNull(); + expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); + expect(rrdom.mirror.getId(rrNode)).toEqual(-1); + expect(mirror.getId(div.shadowRoot)).toEqual(-1); + expect(rrNode.RRNodeType).toEqual(RRNodeType.Element); + expect((rrNode as RRElement).tagName).toEqual('SHADOWROOT'); + expect(rrNode).toBe(parentRRNode.shadowRoot); + }); + }); + + describe('create a RRDocument from a html document', () => { + let browser: puppeteer.Browser; + let code: string; + let page: puppeteer.Page; + + beforeAll(async () => { + browser = await puppeteer.launch(); + const bundle = await rollup.rollup({ + input: path.resolve(__dirname, '../src/virtual-dom.ts'), + plugins: [ + resolve(), + (_typescript({ + tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, + }) as unknown) as rollup.Plugin, + ], + }); + const { + output: [{ code: _code }], + } = await bundle.generate({ + name: 'rrdom', + format: 'iife', + }); + code = _code; + }); + afterAll(async () => { + await browser.close(); + }); + + beforeEach(async () => { + page = await browser.newPage(); + await page.goto('about:blank'); + await page.evaluate(code + printRRDomCode); + }); + + afterEach(async () => { + await page.close(); + }); + it('can build from a common html', async () => { + await page.setContent(getHtml('main.html')); + const result = await page.evaluate(` + const doc = new rrdom.RRDocument(); + rrdom.buildFromDom(document, undefined, doc); + printRRDom(doc, doc.mirror); + `); + expect(result).toMatchSnapshot(); + }); + + it('can build from an iframe html ', async () => { + await page.setContent(getHtml('iframe.html')); + const result = await page.evaluate(` + const doc = new rrdom.RRDocument(); + rrdom.buildFromDom(document, undefined, doc); + printRRDom(doc, doc.mirror); + `); + expect(result).toMatchSnapshot(); + }); + + it('can build from a html containing nested shadow doms', async () => { + await page.setContent(getHtml('shadow-dom.html')); + const result = await page.evaluate(` + const doc = new rrdom.RRDocument(); + rrdom.buildFromDom(document, undefined, doc); + printRRDom(doc, doc.mirror); + `); + expect(result).toMatchSnapshot(); + }); + + it('can build from a xml page', async () => { + const result = await page.evaluate(` + var docu = new DOMParser().parseFromString('', 'application/xml'); + var cdata = docu.createCDATASection('Some data & then some'); + docu.getElementsByTagName('xml')[0].appendChild(cdata); + // Displays: data & then some]]> + + const doc = new rrdom.RRDocument(); + rrdom.buildFromDom(docu, undefined, doc); + printRRDom(doc, doc.mirror); + `); + expect(result).toMatchSnapshot(); + }); + }); + + describe('RRDocument build for virtual dom', () => { + it('can access a unique, decremented unserializedId every time', () => { + const node = new RRDocument(); + for (let i = 1; i <= 100; i++) expect(node.unserializedId).toBe(-i); + }); + + it('can create a new RRDocument', () => { + const dom = new RRDocument(); + const newDom = dom.createDocument('', ''); + expect(newDom).toBeInstanceOf(RRDocument); + }); + + it('can create a new RRDocument receiving a mirror parameter', () => { + const mirror = createMirror(); + const dom = new RRDocument(mirror); + const newDom = dom.createDocument('', ''); + expect(newDom).toBeInstanceOf(RRDocument); + expect(dom.mirror).toBe(mirror); + }); + + it('can build a RRDocument from a real Dom', () => { + const result = buildFromDom(document, mirror); + expect(result.childNodes.length).toBe(2); + expect(result.documentElement).toBeDefined(); + expect(result.head).toBeDefined(); + expect(result.head!.tagName).toBe('HEAD'); + expect(result.body).toBeDefined(); + expect(result.body!.tagName).toBe('BODY'); + }); + + it('can destroy a RRDocument tree', () => { + const dom = new RRDocument(); + const node1 = dom.createDocumentType('', '', ''); + dom.appendChild(node1); + dom.mirror.add(node1, { + id: 0, + type: NodeType.DocumentType, + name: '', + publicId: '', + systemId: '', + }); + const node2 = dom.createElement('html'); + dom.appendChild(node2); + dom.mirror.add(node1, { + id: 1, + type: NodeType.Document, + childNodes: [], + }); + + expect(dom.childNodes.length).toEqual(2); + expect(dom.mirror.has(0)).toBeTruthy(); + expect(dom.mirror.has(1)).toBeTruthy(); + + dom.destroyTree(); + expect(dom.childNodes.length).toEqual(0); + expect(dom.mirror.has(0)).toBeFalsy(); + expect(dom.mirror.has(1)).toBeFalsy(); + }); + + it('can close and open a RRDocument', () => { + const dom = new RRDocument(); + const documentType = dom.createDocumentType('html', '', ''); + dom.appendChild(documentType); + expect(dom.childNodes[0]).toBe(documentType); + expect(dom.unserializedId).toBe(-1); + expect(dom.unserializedId).toBe(-2); + expect(dom.close()); + expect(dom.open()); + expect(dom.childNodes.length).toEqual(0); + expect(dom.unserializedId).toBe(-1); + }); + + it('can execute a dummy getContext function in RRCanvasElement', () => { + const canvas = new RRCanvasElement('CANVAS'); + expect(canvas.getContext).toBeDefined(); + expect(canvas.getContext()).toBeNull(); + }); + + describe('Mirror in the RRDocument', () => { + it('should have a mirror to store id and node', () => { + const dom = new RRDocument(); + expect(dom.mirror).toBeDefined(); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + + const node2 = dom.createTextNode('text'); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + + expect(dom.mirror.getNode(0)).toBe(node1); + expect(dom.mirror.getNode(1)).toBe(node2); + expect(dom.mirror.getNode(2)).toBeNull(); + expect(dom.mirror.getNode(-1)).toBeNull(); + }); + + it('can get node id', () => { + const dom = new RRDocument(); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + + expect(dom.mirror.getId(node1)).toEqual(0); + const node2 = dom.createTextNode('text'); + expect(dom.mirror.getId(node2)).toEqual(-1); + expect(dom.mirror.getId((null as unknown) as RRNode)).toEqual(-1); + }); + + it('has() should return whether the mirror has an ID', () => { + const dom = new RRDocument(); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + const node2 = dom.createTextNode('text'); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + expect(dom.mirror.has(0)).toBeTruthy(); + expect(dom.mirror.has(1)).toBeTruthy(); + expect(dom.mirror.has(2)).toBeFalsy(); + expect(dom.mirror.has(-1)).toBeFalsy(); + }); + + it('can remove node from the mirror', () => { + const dom = new RRDocument(); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + const node2 = dom.createTextNode('text'); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + node1.appendChild(node2); + expect(dom.mirror.has(0)).toBeTruthy(); + expect(dom.mirror.has(1)).toBeTruthy(); + dom.mirror.removeNodeFromMap(node2); + expect(dom.mirror.has(0)).toBeTruthy(); + expect(dom.mirror.has(1)).toBeFalsy(); + + dom.mirror.add(node2, getDefaultSN(node2, 1)); + expect(dom.mirror.has(1)).toBeTruthy(); + // To remove node1 and its child node2 from the mirror. + dom.mirror.removeNodeFromMap(node1); + expect(dom.mirror.has(0)).toBeFalsy(); + expect(dom.mirror.has(1)).toBeFalsy(); + }); + + it('can reset the mirror', () => { + const dom = new RRDocument(); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + const node2 = dom.createTextNode('text'); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + expect(dom.mirror.has(0)).toBeTruthy(); + expect(dom.mirror.has(1)).toBeTruthy(); + + dom.mirror.reset(); + expect(dom.mirror.has(0)).toBeFalsy(); + expect(dom.mirror.has(1)).toBeFalsy(); + }); + + it('hasNode() should return whether the mirror has a node', () => { + const dom = new RRDocument(); + const node1 = dom.createElement('div'); + const node2 = dom.createTextNode('text'); + expect(dom.mirror.hasNode(node1)).toBeFalsy(); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + expect(dom.mirror.hasNode(node1)).toBeTruthy(); + expect(dom.mirror.hasNode(node2)).toBeFalsy(); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + expect(dom.mirror.hasNode(node2)).toBeTruthy(); + }); + + it('can get all IDs from the mirror', () => { + const dom = new RRDocument(); + expect(dom.mirror.getIds().length).toBe(0); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + const node2 = dom.createTextNode('text'); + dom.mirror.add(node2, getDefaultSN(node2, 1)); + expect(dom.mirror.getIds().length).toBe(2); + expect(dom.mirror.getIds()).toStrictEqual([0, 1]); + }); + + it('can replace nodes', () => { + const dom = new RRDocument(); + expect(dom.mirror.getIds().length).toBe(0); + const node1 = dom.createElement('div'); + dom.mirror.add(node1, getDefaultSN(node1, 0)); + expect(dom.mirror.getNode(0)).toBe(node1); + const node2 = dom.createTextNode('text'); + dom.mirror.replace(0, node2); + expect(dom.mirror.getNode(0)).toBe(node2); + }); + }); + }); + + describe('can get default SN value from a RRNode', () => { + const rrdom = new RRDocument(); + it('can get from RRDocument', () => { + const node = rrdom; + const sn = getDefaultSN(node, 1); + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.Document); + expect((sn as documentNode).childNodes).toBeInstanceOf(Array); + }); + + it('can get from RRDocumentType', () => { + const name = 'name', + publicId = 'publicId', + systemId = 'systemId'; + const node = rrdom.createDocumentType(name, publicId, systemId); + const sn = getDefaultSN(node, 1); + + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.DocumentType); + expect((sn as documentTypeNode).name).toEqual(name); + expect((sn as documentTypeNode).publicId).toEqual(publicId); + expect((sn as documentTypeNode).systemId).toEqual(systemId); + }); + + it('can get from RRElement', () => { + const node = rrdom.createElement('div'); + const sn = getDefaultSN(node, 1); + + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.Element); + expect((sn as elementNode).tagName).toEqual('div'); + expect((sn as elementNode).attributes).toBeDefined(); + expect((sn as elementNode).childNodes).toBeInstanceOf(Array); + }); + + it('can get from RRText', () => { + const node = rrdom.createTextNode('text'); + const sn = getDefaultSN(node, 1); + + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.Text); + expect((sn as textNode).textContent).toEqual('text'); + }); + + it('can get from RRComment', () => { + const node = rrdom.createComment('comment'); + const sn = getDefaultSN(node, 1); + + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.Comment); + expect((sn as commentNode).textContent).toEqual('comment'); + }); + + it('can get from RRCDATASection', () => { + const node = rrdom.createCDATASection('data'); + const sn = getDefaultSN(node, 1); + + expect(sn).toBeDefined(); + expect(sn.type).toEqual(RRNodeType.CDATA); + expect((sn as cdataNode).textContent).toEqual(''); + }); + }); +}); +function getHtml(fileName: string) { + const filePath = path.resolve(__dirname, `./html/${fileName}`); + return fs.readFileSync(filePath, 'utf8'); +} diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 15e1aac33c..4a4f18a080 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ES6", "module": "commonjs", "noImplicitAny": true, "strictNullChecks": true, @@ -11,9 +11,10 @@ "outDir": "build", "lib": ["es6", "dom"], "skipLibCheck": true, - "declaration": true + "declaration": true, + "importsNotUsedAsValues": "error" }, "compileOnSave": true, "exclude": ["test"], - "include": ["src", "test.d.ts"] + "include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"] } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 55a830e42f..2824bb6170 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -2,26 +2,28 @@ "name": "rrweb-player", "version": "0.7.14", "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.2", - "@rollup/plugin-node-resolve": "^7.0.0", - "@rollup/plugin-typescript": "^4.0.0", + "@rollup/plugin-commonjs": "^22.0.0", + "@rollup/plugin-node-resolve": "^13.2.1", + "@rollup/plugin-typescript": "^8.3.2", + "@types/offscreencanvas": "^2019.6.4", "@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/parser": "^3.7.0", "eslint": "^7.5.0", "eslint-config-google": "^0.11.0", "eslint-plugin-svelte3": "^2.7.3", "postcss-easy-import": "^3.0.0", - "rollup": "^2.45.2", + "rollup": "^2.71.1", "rollup-plugin-css-only": "^3.1.0", "rollup-plugin-livereload": "^2.0.0", "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-web-worker-loader": "^1.6.1", "sirv-cli": "^0.4.4", "svelte": "^3.2.0", "svelte-check": "^1.4.0", "svelte-preprocess": "^4.0.0", "tslib": "^2.0.0", - "typescript": "^3.9.7" + "typescript": "^4.6.4" }, "dependencies": { "@tsconfig/svelte": "^1.0.0", diff --git a/packages/rrweb-player/rollup.config.js b/packages/rrweb-player/rollup.config.js index 313b8e1133..403bf883d2 100644 --- a/packages/rrweb-player/rollup.config.js +++ b/packages/rrweb-player/rollup.config.js @@ -4,6 +4,7 @@ import commonjs from '@rollup/plugin-commonjs'; import livereload from 'rollup-plugin-livereload'; import { terser } from 'rollup-plugin-terser'; import sveltePreprocess from 'svelte-preprocess'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; import typescript from '@rollup/plugin-typescript'; import pkg from './package.json'; import css from 'rollup-plugin-css-only'; @@ -64,8 +65,12 @@ export default entries.map((output) => ({ browser: true, dedupe: ['svelte'], }), + commonjs(), + // supports bundling `web-worker:..filename` from rrweb + webWorkerLoader(), + typescript(), css({ diff --git a/packages/rrweb-player/tsconfig.json b/packages/rrweb-player/tsconfig.json index 77db65da2f..b049c15dd8 100644 --- a/packages/rrweb-player/tsconfig.json +++ b/packages/rrweb-player/tsconfig.json @@ -1,5 +1,10 @@ { "extends": "@tsconfig/svelte/tsconfig.json", "include": ["src/**/*"], - "exclude": ["node_modules/*", "__sapper__/*", "public/*"], -} \ No newline at end of file + "exclude": [ + "node_modules/*", + "__sapper__/*", + "public/*", + "../rrweb/src/record/workers/workers.d.ts" + ] +} diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index cf818fc7e3..9e7440cba6 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -330,7 +330,7 @@ export function buildNodeWithSN( if (n.rootId) { console.assert( (mirror.getNode(n.rootId) as Document) === doc, - 'Target document should has the same root id.', + 'Target document should have the same root id.', ); } // use target document as root document diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 89b4e1ac12..6a09c633b4 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -76,6 +76,28 @@ export interface ICanvas extends HTMLCanvasElement { __context: string; } +export interface IMirror { + getId(n: TNode | undefined | null): number; + + getNode(id: number): TNode | null; + + getIds(): number[]; + + getMeta(n: TNode): serializedNodeWithId | null; + + removeNodeFromMap(n: TNode): void; + + has(id: number): boolean; + + hasNode(node: TNode): boolean; + + add(n: TNode, meta: serializedNodeWithId): void; + + replace(id: number, n: TNode): void; + + reset(): void; +} + export type idNodeMap = Map; export type nodeMetaMap = WeakMap; diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 5eba92f806..a2bde1570c 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -3,6 +3,7 @@ import { MaskInputFn, MaskInputOptions, nodeMetaMap, + IMirror, serializedNodeWithId, } from './types'; @@ -15,7 +16,7 @@ export function isShadowRoot(n: Node): n is ShadowRoot { return Boolean(host && host.shadowRoot && host.shadowRoot === n); } -export class Mirror { +export class Mirror implements IMirror { private idNodeMap: idNodeMap = new Map(); private nodeMetaMap: nodeMetaMap = new WeakMap(); @@ -47,7 +48,9 @@ export class Mirror { this.idNodeMap.delete(id); if (n.childNodes) { - n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); + n.childNodes.forEach((childNode) => + this.removeNodeFromMap((childNode as unknown) as Node), + ); } } has(id: number): boolean { diff --git a/packages/rrweb-snapshot/typings/types.d.ts b/packages/rrweb-snapshot/typings/types.d.ts index 9ffd0ea8ec..bac3fcfb1f 100644 --- a/packages/rrweb-snapshot/typings/types.d.ts +++ b/packages/rrweb-snapshot/typings/types.d.ts @@ -58,6 +58,18 @@ export interface INode extends Node { export interface ICanvas extends HTMLCanvasElement { __context: string; } +export interface IMirror { + getId(n: TNode | undefined | null): number; + getNode(id: number): TNode | null; + getIds(): number[]; + getMeta(n: TNode): serializedNodeWithId | null; + removeNodeFromMap(n: TNode): void; + has(id: number): boolean; + hasNode(node: TNode): boolean; + add(n: TNode, meta: serializedNodeWithId): void; + replace(id: number, n: TNode): void; + reset(): void; +} export declare type idNodeMap = Map; export declare type nodeMetaMap = WeakMap; export declare type MaskInputOptions = Partial<{ diff --git a/packages/rrweb-snapshot/typings/utils.d.ts b/packages/rrweb-snapshot/typings/utils.d.ts index c3bf88f824..23ae46f7ea 100644 --- a/packages/rrweb-snapshot/typings/utils.d.ts +++ b/packages/rrweb-snapshot/typings/utils.d.ts @@ -1,7 +1,7 @@ -import { MaskInputFn, MaskInputOptions, serializedNodeWithId } from './types'; +import { MaskInputFn, MaskInputOptions, IMirror, serializedNodeWithId } from './types'; export declare function isElement(n: Node): n is Element; export declare function isShadowRoot(n: Node): n is ShadowRoot; -export declare class Mirror { +export declare class Mirror implements IMirror { private idNodeMap; private nodeMetaMap; getId(n: Node | undefined | null): number; diff --git a/packages/rrweb/jest.config.js b/packages/rrweb/jest.config.js index 29db4e7fa0..c1f271c9b7 100644 --- a/packages/rrweb/jest.config.js +++ b/packages/rrweb/jest.config.js @@ -5,5 +5,6 @@ module.exports = { testMatch: ['**/**.test.ts'], moduleNameMapper: { '\\.css$': 'identity-obj-proxy', + 'rrdom/es/(.*)': 'rrdom/lib/$1', }, }; diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 18b32fe1b4..72b6ffff89 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -46,12 +46,12 @@ "@types/inquirer": "0.0.43", "@types/jest": "^27.4.1", "@types/jest-image-snapshot": "^4.3.1", - "@types/jsdom": "^16.2.14", "@types/node": "^17.0.21", "@types/offscreencanvas": "^2019.6.4", "@types/prettier": "^2.3.2", "@types/puppeteer": "^5.4.4", "cross-env": "^5.2.0", + "esbuild": "^0.14.38", "fast-mhtml": "^1.1.9", "identity-obj-proxy": "^3.0.0", "ignore-styles": "^5.0.1", @@ -59,14 +59,12 @@ "jest": "^27.5.1", "jest-image-snapshot": "^4.5.1", "jest-snapshot": "^23.6.0", - "jsdom": "^17.0.0", - "jsdom-global": "^3.0.2", "prettier": "2.2.1", "puppeteer": "^9.1.1", "rollup": "^2.68.0", + "rollup-plugin-esbuild": "^4.9.1", "rollup-plugin-postcss": "^3.1.1", "rollup-plugin-rename-node-modules": "^1.3.1", - "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", @@ -81,6 +79,7 @@ "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^1.1.3", + "rrdom": "^0.1.2", "rrweb-snapshot": "^1.1.14" } } diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index 3c2eff47a1..fe4ec73171 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -1,6 +1,6 @@ import typescript from 'rollup-plugin-typescript2'; +import esbuild from 'rollup-plugin-esbuild'; import resolve from '@rollup/plugin-node-resolve'; -import { terser } from 'rollup-plugin-terser'; import postcss from 'rollup-plugin-postcss'; import renameNodeModules from 'rollup-plugin-rename-node-modules'; import webWorkerLoader from 'rollup-plugin-web-worker-loader'; @@ -108,10 +108,34 @@ const baseConfigs = [ let configs = []; +function getPlugins(options = {}) { + const { minify = false, sourceMap = false } = options; + return [ + resolve({ browser: true }), + webWorkerLoader({ + targetPlatform: 'browser', + inline: true, + sourceMap, + }), + esbuild({ + minify, + }), + postcss({ + extract: false, + inject: false, + minimize: minify, + sourceMap, + }), + ]; +} + for (const c of baseConfigs) { const basePlugins = [ resolve({ browser: true }), + + // supports bundling `web-worker:..filename` webWorkerLoader(), + typescript(), ]; const plugins = basePlugins.concat( @@ -123,7 +147,7 @@ for (const c of baseConfigs) { // browser configs.push({ input: c.input, - plugins, + plugins: getPlugins(), output: [ { name: c.name, @@ -135,14 +159,7 @@ for (const c of baseConfigs) { // browser + minify configs.push({ input: c.input, - plugins: basePlugins.concat( - postcss({ - extract: true, - minimize: true, - sourceMap: true, - }), - terser(), - ), + plugins: getPlugins({ minify: true, sourceMap: true }), output: [ { name: c.name, @@ -197,23 +214,9 @@ if (process.env.BROWSER_ONLY) { configs = []; for (const c of browserOnlyBaseConfigs) { - const plugins = [ - resolve({ browser: true }), - webWorkerLoader(), - typescript({ - outDir: null, - }), - postcss({ - extract: false, - inject: false, - sourceMap: true, - }), - terser(), - ]; - configs.push({ input: c.input, - plugins, + plugins: getPlugins({ sourceMap: true, minify: true }), output: [ { name: c.name, diff --git a/packages/rrweb/src/packer/base.ts b/packages/rrweb/src/packer/base.ts index ef15efb6cf..00cf3748e6 100644 --- a/packages/rrweb/src/packer/base.ts +++ b/packages/rrweb/src/packer/base.ts @@ -1,4 +1,4 @@ -import { eventWithTime } from '../types'; +import type { eventWithTime } from '../types'; export type PackFn = (event: eventWithTime) => string; export type UnpackFn = (raw: string) => eventWithTime; diff --git a/packages/rrweb/src/packer/unpack.ts b/packages/rrweb/src/packer/unpack.ts index 9211d7b593..c224e09372 100644 --- a/packages/rrweb/src/packer/unpack.ts +++ b/packages/rrweb/src/packer/unpack.ts @@ -1,6 +1,6 @@ import { strFromU8, strToU8, unzlibSync } from 'fflate'; import { UnpackFn, eventWithTimeAndPacker, MARK } from './base'; -import { eventWithTime } from '../types'; +import type { eventWithTime } from '../types'; export const unpack: UnpackFn = (raw: string) => { if (typeof raw !== 'string') { @@ -16,7 +16,7 @@ export const unpack: UnpackFn = (raw: string) => { } try { const e: eventWithTimeAndPacker = JSON.parse( - strFromU8(unzlibSync(strToU8(raw, true))) + strFromU8(unzlibSync(strToU8(raw, true))), ); if (e.v === MARK) { return e; diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 55b81d0f95..914e3057a8 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -1,4 +1,4 @@ -import { listenerHandler, RecordPlugin, IWindow } from '../../../types'; +import type { listenerHandler, RecordPlugin, IWindow } from '../../../types'; import { patch } from '../../../utils'; import { ErrorStackParser, StackFrame } from './error-stack-parser'; import { stringify } from './stringify'; diff --git a/packages/rrweb/src/plugins/console/record/stringify.ts b/packages/rrweb/src/plugins/console/record/stringify.ts index 3804d48e88..e32069ff9f 100644 --- a/packages/rrweb/src/plugins/console/record/stringify.ts +++ b/packages/rrweb/src/plugins/console/record/stringify.ts @@ -4,7 +4,7 @@ * */ -import { StringifyOptions } from './index'; +import type { StringifyOptions } from './index'; /** * transfer the node path in Event to string diff --git a/packages/rrweb/src/plugins/sequential-id/record/index.ts b/packages/rrweb/src/plugins/sequential-id/record/index.ts index a439831191..fa5a616cae 100644 --- a/packages/rrweb/src/plugins/sequential-id/record/index.ts +++ b/packages/rrweb/src/plugins/sequential-id/record/index.ts @@ -1,4 +1,4 @@ -import { RecordPlugin } from '../../../types'; +import type { RecordPlugin } from '../../../types'; export type SequentialIdOptions = { key: string; diff --git a/packages/rrweb/src/plugins/sequential-id/replay/index.ts b/packages/rrweb/src/plugins/sequential-id/replay/index.ts index 852a02cf36..7e3644afa7 100644 --- a/packages/rrweb/src/plugins/sequential-id/replay/index.ts +++ b/packages/rrweb/src/plugins/sequential-id/replay/index.ts @@ -1,5 +1,5 @@ import type { SequentialIdOptions } from '../record'; -import { ReplayPlugin, eventWithTime } from '../../../types'; +import type { ReplayPlugin, eventWithTime } from '../../../types'; type Options = SequentialIdOptions & { warnOnMissingId: boolean; diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index e4331788d4..db2d85d44d 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -1,5 +1,5 @@ -import { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import { mutationCallBack } from '../types'; +import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; +import type { mutationCallBack } from '../types'; export class IframeManager { private iframes: WeakMap = new WeakMap(); diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index aaa8900550..80718f2589 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -7,7 +7,7 @@ import { maskInputValue, Mirror, } from 'rrweb-snapshot'; -import { +import type { mutationRecord, textCursor, attributeCursor, @@ -298,7 +298,7 @@ export default class MutationBuffer { inlineImages: this.inlineImages, onSerialize: (currentN) => { if (isSerializedIframe(currentN, this.mirror)) { - this.iframeManager.addIframe(currentN); + this.iframeManager.addIframe(currentN as HTMLIFrameElement); } if (hasShadowRoot(n)) { this.shadowDomManager.addShadowRoot(n.shadowRoot, document); @@ -322,7 +322,7 @@ export default class MutationBuffer { this.mirror.removeNodeFromMap(this.mapRemoves.shift()!); } - for (const n of this.movedSet) { + for (const n of Array.from(this.movedSet.values())) { if ( isParentRemoved(this.removes, n, this.mirror) && !this.movedSet.has(n.parentNode!) @@ -332,7 +332,7 @@ export default class MutationBuffer { pushAdd(n); } - for (const n of this.addedSet) { + for (const n of Array.from(this.addedSet.values())) { if ( !isAncestorInSet(this.droppedSet, n) && !isParentRemoved(this.removes, n, this.mirror) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 5504377853..fc1201ced6 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -1,5 +1,5 @@ import { MaskInputOptions, maskInputValue } from 'rrweb-snapshot'; -import { FontFaceSet } from 'css-font-loading-module'; +import type { FontFaceSet } from 'css-font-loading-module'; import { throttle, on, @@ -108,9 +108,9 @@ export function initMutationObserver( typeof MutationObserver >)[angularZoneSymbol]; } - const observer = new mutationObserverCtor( - mutationBuffer.processMutations.bind(mutationBuffer), - ); + const observer = new (mutationObserverCtor as new ( + callback: MutationCallback, + ) => MutationObserver)(mutationBuffer.processMutations.bind(mutationBuffer)); observer.observe(rootEl, { attributes: true, attributeOldValue: true, diff --git a/packages/rrweb/src/record/observers/canvas/2d.ts b/packages/rrweb/src/record/observers/canvas/2d.ts index 86cf0e396a..6309a69e3c 100644 --- a/packages/rrweb/src/record/observers/canvas/2d.ts +++ b/packages/rrweb/src/record/observers/canvas/2d.ts @@ -1,4 +1,4 @@ -import { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; import { blockClass, CanvasContext, diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index 79859dbbc8..347be47375 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -1,7 +1,6 @@ -import { ICanvas, Mirror } from 'rrweb-snapshot'; -import { +import type { ICanvas, Mirror } from 'rrweb-snapshot'; +import type { blockClass, - CanvasContext, canvasManagerMutationCallback, canvasMutationCallback, canvasMutationCommand, @@ -10,11 +9,12 @@ import { listenerHandler, CanvasArg, } from '../../../types'; +import { CanvasContext } from '../../../types'; import initCanvas2DMutationObserver from './2d'; import initCanvasContextObserver from './canvas'; import initCanvasWebGLMutationObserver from './webgl'; import ImageBitmapDataURLWorker from 'web-worker:../../workers/image-bitmap-data-url-worker.ts'; -import { ImageBitmapDataURLRequestWorker } from '../../workers/image-bitmap-data-url-worker'; +import type { ImageBitmapDataURLRequestWorker } from '../../workers/image-bitmap-data-url-worker'; export type RafStamps = { latestId: number; invokeId: number | null }; diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index ff42c7a05d..74a9f8137a 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -1,5 +1,5 @@ -import { ICanvas } from 'rrweb-snapshot'; -import { blockClass, IWindow, listenerHandler } from '../../../types'; +import type { ICanvas } from 'rrweb-snapshot'; +import type { blockClass, IWindow, listenerHandler } from '../../../types'; import { isBlocked, patch } from '../../../utils'; export default function initCanvasContextObserver( diff --git a/packages/rrweb/src/record/observers/canvas/serialize-args.ts b/packages/rrweb/src/record/observers/canvas/serialize-args.ts index 98095b3a61..f310b8e5d9 100644 --- a/packages/rrweb/src/record/observers/canvas/serialize-args.ts +++ b/packages/rrweb/src/record/observers/canvas/serialize-args.ts @@ -1,5 +1,5 @@ import { encode } from 'base64-arraybuffer'; -import { IWindow, CanvasArg } from '../../../types'; +import type { IWindow, CanvasArg } from '../../../types'; // TODO: unify with `replay/webgl.ts` type CanvasVarMap = Map; diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index 1ebe2e19da..f12a104db1 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -1,4 +1,4 @@ -import { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; import { blockClass, CanvasContext, @@ -31,8 +31,8 @@ function patchGLPrototype( return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); saveWebGLVar(result, win, prototype); - if (!isBlocked(this.canvas, blockClass)) { - const id = mirror.getId(this.canvas); + if (!isBlocked(this.canvas as HTMLCanvasElement, blockClass)) { + const id = mirror.getId(this.canvas as HTMLCanvasElement); const recordArgs = serializeArgs([...args], win, prototype); const mutation: canvasMutationWithType = { diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index 1bc50d0f2e..9e0b91e080 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -1,4 +1,4 @@ -import { +import type { mutationCallBack, scrollCallback, MutationBufferParam, @@ -6,7 +6,7 @@ import { } from '../types'; import { initMutationObserver, initScrollObserver } from './observer'; import { patch } from '../utils'; -import { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; type BypassOptions = Omit< MutationBufferParam, diff --git a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts index 28cc5c8b72..f4ffaa5753 100644 --- a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts +++ b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts @@ -1,5 +1,5 @@ import { encode } from 'base64-arraybuffer'; -import { +import type { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse, } from '../../types'; diff --git a/packages/rrweb/src/replay/canvas/2d.ts b/packages/rrweb/src/replay/canvas/2d.ts index f535f24bac..53668659fd 100644 --- a/packages/rrweb/src/replay/canvas/2d.ts +++ b/packages/rrweb/src/replay/canvas/2d.ts @@ -1,5 +1,5 @@ -import { Replayer } from '../'; -import { canvasMutationCommand } from '../../types'; +import type { Replayer } from '../'; +import type { canvasMutationCommand } from '../../types'; import { deserializeArg } from './deserialize-args'; export default async function canvasMutation({ diff --git a/packages/rrweb/src/replay/canvas/deserialize-args.ts b/packages/rrweb/src/replay/canvas/deserialize-args.ts index d5adcbbf7d..27f12b920e 100644 --- a/packages/rrweb/src/replay/canvas/deserialize-args.ts +++ b/packages/rrweb/src/replay/canvas/deserialize-args.ts @@ -1,6 +1,6 @@ import { decode } from 'base64-arraybuffer'; import type { Replayer } from '../'; -import { CanvasArg, SerializedCanvasArg } from '../../types'; +import type { CanvasArg, SerializedCanvasArg } from '../../types'; // TODO: add ability to wipe this list type GLVarMap = Map; diff --git a/packages/rrweb/src/replay/canvas/index.ts b/packages/rrweb/src/replay/canvas/index.ts index 8924b9ec31..b192500a39 100644 --- a/packages/rrweb/src/replay/canvas/index.ts +++ b/packages/rrweb/src/replay/canvas/index.ts @@ -1,4 +1,4 @@ -import { Replayer } from '..'; +import type { Replayer } from '..'; import { CanvasContext, canvasMutationCommand, diff --git a/packages/rrweb/src/replay/canvas/webgl.ts b/packages/rrweb/src/replay/canvas/webgl.ts index 67bffb6ff8..4480cd9592 100644 --- a/packages/rrweb/src/replay/canvas/webgl.ts +++ b/packages/rrweb/src/replay/canvas/webgl.ts @@ -11,9 +11,8 @@ function getContext( // you might have to do `ctx.flush()` before every webgl canvas event try { if (type === CanvasContext.WebGL) { - return ( - target.getContext('webgl')! || target.getContext('experimental-webgl') - ); + return (target.getContext('webgl')! || + target.getContext('experimental-webgl')) as WebGLRenderingContext; } return target.getContext('webgl2')!; } catch (e) { diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 9c4fe9751a..1e2ddcc0dd 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -7,6 +7,26 @@ import { Mirror, createMirror, } from 'rrweb-snapshot'; +import { + RRDocument, + StyleRuleType, + createOrGetNode, + buildFromNode, + buildFromDom, + diff, + getDefaultSN, +} from 'rrdom/es/virtual-dom'; +import type { + RRNode, + RRElement, + RRStyleElement, + RRIFrameElement, + RRMediaElement, + RRCanvasElement, + ReplayerHandler, + Mirror as RRDOMMirror, + VirtualStyleRules, +} from 'rrdom/es/virtual-dom'; import * as mittProxy from 'mitt'; import { polyfill as smoothscrollPolyfill } from './smoothscroll'; import { Timer } from './timer'; @@ -34,37 +54,28 @@ import { scrollData, inputData, canvasMutationData, - ElementState, styleAttributeValue, styleValueWithPriority, mouseMovePos, IWindow, canvasMutationCommand, canvasMutationParam, - textMutation, + canvasEventWithTime, } from '../types'; import { polyfill, - TreeIndex, queueToResolveTrees, iterateResolveTree, AppendedIframe, getBaseDimension, hasShadowRoot, isSerializedIframe, + getNestedRule, + getPositionsAndIndex, uniqueTextMutations, } from '../utils'; import getInjectStyleRules from './styles/inject-style'; import './styles/style.css'; -import { - applyVirtualStyleRulesToNode, - storeCSSRules, - StyleRuleType, - VirtualStyleRules, - VirtualStyleRulesMap, - getNestedRule, - getPositionsAndIndex, -} from './virtual-styles'; import canvasMutation from './canvas'; import { deserializeArg } from './canvas/deserialize-args'; @@ -105,6 +116,10 @@ export class Replayer { public config: playerConfig; + // In the fast-forward process, if the virtual-dom optimization is used, this flag value is true. + public usingVirtualDom = false; + public virtualDom: RRDocument = new RRDocument(); + private mouse: HTMLDivElement; private mouseTail: HTMLCanvasElement | null = null; private tailPositions: Array<{ x: number; y: number }> = []; @@ -116,12 +131,6 @@ export class Replayer { // tslint:disable-next-line: variable-name private legacy_missingNodeRetryMap: missingNodeMap = {}; - private treeIndex!: TreeIndex; - private fragmentParentMap!: Map; - private elementStateMap!: Map; - // Hold the list of CSSRules for in-memory state restoration - private virtualStyleRulesMap!: VirtualStyleRulesMap; - // The replayer uses the cache to speed up replay and scrubbing. private cache: BuildCache = createCache(); @@ -159,6 +168,7 @@ export class Replayer { UNSAFE_replayCanvas: false, pauseAnimation: true, mouseTail: defaultMouseTailConfig, + useVirtualDom: true, // Virtual-dom optimization is enabled by default. }; this.config = Object.assign({}, defaultConfig, config); @@ -169,38 +179,72 @@ export class Replayer { this.setupDom(); - this.treeIndex = new TreeIndex(); - this.fragmentParentMap = new Map(); - this.elementStateMap = new Map(); - this.virtualStyleRulesMap = new Map(); - this.emitter.on(ReplayerEvents.Flush, () => { - const { scrollMap, inputMap, mutationData } = this.treeIndex.flush(); - - this.fragmentParentMap.forEach((parent, frag) => - this.restoreRealParent(frag, parent), - ); - - // apply text needs to happen before virtual style rules gets applied - // as it can overwrite the contents of a stylesheet - for (const d of uniqueTextMutations(mutationData.texts)) { - this.applyText(d, mutationData); - } + if (this.usingVirtualDom) { + const replayerHandler: ReplayerHandler = { + mirror: this.mirror, + applyCanvas: ( + canvasEvent: canvasEventWithTime, + canvasMutationData: canvasMutationData, + target: HTMLCanvasElement, + ) => { + canvasMutation({ + event: canvasEvent, + mutation: canvasMutationData, + target, + imageMap: this.imageMap, + canvasEventMap: this.canvasEventMap, + errorHandler: this.warnCanvasMutationFailed.bind(this), + }); + }, + applyInput: this.applyInput.bind(this), + applyScroll: this.applyScroll.bind(this), + }; + diff( + this.iframe.contentDocument!, + this.virtualDom, + replayerHandler, + this.virtualDom.mirror, + ); + this.virtualDom.destroyTree(); + this.usingVirtualDom = false; - for (const node of this.virtualStyleRulesMap.keys()) { - // restore css rules of style elements after they are mounted - this.restoreNodeSheet(node); + // If these legacy missing nodes haven't been resolved, they should be converted to real Nodes. + if (Object.keys(this.legacy_missingNodeRetryMap).length) { + for (const key in this.legacy_missingNodeRetryMap) { + try { + const value = this.legacy_missingNodeRetryMap[key]; + const realNode = createOrGetNode( + value.node as RRNode, + this.mirror, + this.virtualDom.mirror, + ); + diff( + realNode, + value.node as RRNode, + replayerHandler, + this.virtualDom.mirror, + ); + value.node = realNode; + } catch (error) { + if (this.config.showWarning) { + console.warn(error); + } + } + } + } } - this.fragmentParentMap.clear(); - this.elementStateMap.clear(); - this.virtualStyleRulesMap.clear(); - for (const d of scrollMap.values()) { - this.applyScroll(d, true); - } - for (const d of inputMap.values()) { - this.applyInput(d); + if (this.mousePos) { + this.moveAndHover( + this.mousePos.x, + this.mousePos.y, + this.mousePos.id, + true, + this.mousePos.debugData, + ); } + this.mousePos = null; }); this.emitter.on(ReplayerEvents.PlayBack, () => { this.firstFullSnapshot = null; @@ -371,7 +415,7 @@ export class Replayer { } this.iframe.contentDocument ?.getElementsByTagName('html')[0] - .classList.remove('rrweb-paused'); + ?.classList.remove('rrweb-paused'); this.emitter.emit(ReplayerEvents.Start); } @@ -385,7 +429,7 @@ export class Replayer { } this.iframe.contentDocument ?.getElementsByTagName('html')[0] - .classList.add('rrweb-paused'); + ?.classList.add('rrweb-paused'); this.emitter.emit(ReplayerEvents.Pause); } @@ -488,14 +532,7 @@ export class Replayer { case EventType.FullSnapshot: case EventType.Meta: case EventType.Plugin: - break; case EventType.IncrementalSnapshot: - switch (event.data.source) { - case IncrementalSource.MediaInteraction: - continue; - default: - break; - } break; default: break; @@ -503,16 +540,6 @@ export class Replayer { const castFn = this.getCastFn(event, true); castFn(); } - if (this.mousePos) { - this.moveAndHover( - this.mousePos.x, - this.mousePos.y, - this.mousePos.id, - true, - this.mousePos.debugData, - ); - } - this.mousePos = null; if (this.touchActive === true) { this.mouse.classList.add('touch-active'); } else if (this.touchActive === false) { @@ -692,11 +719,9 @@ export class Replayer { } private insertStyleRules( - documentElement: HTMLElement, - head: HTMLHeadElement, + documentElement: HTMLElement | RRElement, + head: HTMLHeadElement | RRElement, ) { - const styleEl = document.createElement('style'); - documentElement!.insertBefore(styleEl, head); const injectStylesRules = getInjectStyleRules( this.config.blockClass, ).concat(this.config.insertStyleRules); @@ -705,44 +730,64 @@ export class Replayer { 'html.rrweb-paused *, html.rrweb-paused *:before, html.rrweb-paused *:after { animation-play-state: paused !important; }', ); } - for (let idx = 0; idx < injectStylesRules.length; idx++) { - (styleEl.sheet! as CSSStyleSheet).insertRule(injectStylesRules[idx], idx); + if (this.usingVirtualDom) { + const styleEl = this.virtualDom.createElement('style') as RRStyleElement; + this.virtualDom.mirror.add( + styleEl, + getDefaultSN(styleEl, this.virtualDom.unserializedId), + ); + (documentElement as RRElement)!.insertBefore(styleEl, head as RRElement); + for (let idx = 0; idx < injectStylesRules.length; idx++) { + // push virtual styles + styleEl.rules.push({ + cssText: injectStylesRules[idx], + type: StyleRuleType.Insert, + index: idx, + }); + } + } else { + const styleEl = document.createElement('style'); + (documentElement as HTMLElement)!.insertBefore( + styleEl, + head as HTMLHeadElement, + ); + for (let idx = 0; idx < injectStylesRules.length; idx++) { + (styleEl.sheet! as CSSStyleSheet).insertRule( + injectStylesRules[idx], + idx, + ); + } } } private attachDocumentToIframe( mutation: addedNodeMutation, - iframeEl: HTMLIFrameElement, + iframeEl: HTMLIFrameElement | RRIFrameElement, ) { + const mirror: RRDOMMirror | Mirror = this.usingVirtualDom + ? this.virtualDom.mirror + : this.mirror; + type TNode = typeof mirror extends Mirror ? Node : RRNode; + type TMirror = typeof mirror extends Mirror ? Mirror : RRDOMMirror; + const collected: AppendedIframe[] = []; - // If iframeEl is detached from dom, iframeEl.contentDocument is null. - if (!iframeEl.contentDocument) { - let parent = iframeEl.parentNode; - while (parent) { - // The parent of iframeEl is virtual parent and we need to mount it on the dom. - if (this.fragmentParentMap.has(parent)) { - const frag = parent; - const realParent = this.fragmentParentMap.get(frag)!; - this.restoreRealParent(frag, realParent); - break; - } - parent = parent.parentNode; - } - } buildNodeWithSN(mutation.node, { - doc: iframeEl.contentDocument!, - mirror: this.mirror, + doc: iframeEl.contentDocument! as Document, + mirror: mirror as Mirror, hackCss: true, skipChild: false, afterAppend: (builtNode) => { this.collectIframeAndAttachDocument(collected, builtNode); - const sn = this.mirror.getMeta(builtNode); + const sn = (mirror as TMirror).getMeta((builtNode as unknown) as TNode); if ( sn?.type === NodeType.Element && sn?.tagName.toUpperCase() === 'HTML' ) { const { documentElement, head } = iframeEl.contentDocument!; - this.insertStyleRules(documentElement, head); + this.insertStyleRules( + documentElement as HTMLElement | RRElement, + head as HTMLElement | RRElement, + ); } }, cache: this.cache, @@ -764,7 +809,10 @@ export class Replayer { (m) => m.parentId === this.mirror.getId(builtNode), ); if (mutationInQueue) { - collected.push({ mutationInQueue, builtNode }); + collected.push({ + mutationInQueue, + builtNode: builtNode as HTMLIFrameElement, + }); } } } @@ -935,21 +983,6 @@ export class Replayer { const { data: d } = e; switch (d.source) { case IncrementalSource.Mutation: { - if (isSync) { - d.adds.forEach((m) => this.treeIndex.add(m)); - d.texts.forEach((m) => { - const target = this.mirror.getNode(m.id); - const parent = target?.parentNode; - // remove any style rules that pending - // for stylesheets where the contents get replaced - if (parent && this.virtualStyleRulesMap.has(parent)) - this.virtualStyleRulesMap.delete(parent); - - this.treeIndex.text(m); - }); - d.attributes.forEach((m) => this.treeIndex.attribute(m)); - d.removes.forEach((m) => this.treeIndex.remove(m, this.mirror)); - } try { this.applyMutation(d, isSync); } catch (error) { @@ -992,7 +1025,7 @@ export class Replayer { /** * Same as the situation of missing input target. */ - if (d.id === -1) { + if (d.id === -1 || isSync) { break; } const event = new Event(MouseInteractions[d.type].toLowerCase()); @@ -1079,11 +1112,16 @@ export class Replayer { if (d.id === -1) { break; } - if (isSync) { - this.treeIndex.scroll(d); + if (this.usingVirtualDom) { + const target = this.virtualDom.mirror.getNode(d.id) as RRElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + target.scrollData = d; break; } - this.applyScroll(d, false); + // Use isSync rather than this.usingVirtualDom because not every fast-forward process uses virtual dom optimization. + this.applyScroll(d, isSync); break; } case IncrementalSource.ViewportResize: @@ -1102,19 +1140,25 @@ export class Replayer { if (d.id === -1) { break; } - if (isSync) { - this.treeIndex.input(d); + if (this.usingVirtualDom) { + const target = this.virtualDom.mirror.getNode(d.id) as RRElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + target.inputData = d; break; } this.applyInput(d); break; } case IncrementalSource.MediaInteraction: { - const target = this.mirror.getNode(d.id); + const target = this.usingVirtualDom + ? this.virtualDom.mirror.getNode(d.id) + : this.mirror.getNode(d.id); if (!target) { return this.debugNodeNotFound(d, d.id); } - const mediaEl = target as HTMLMediaElement; + const mediaEl = target as HTMLMediaElement | RRMediaElement; try { if (d.currentTime) { mediaEl.currentTime = d.currentTime; @@ -1145,158 +1189,118 @@ export class Replayer { break; } case IncrementalSource.StyleSheetRule: { - const target = this.mirror.getNode(d.id); - if (!target) { - return this.debugNodeNotFound(d, d.id); - } - - const styleEl = target as HTMLStyleElement; - const parent = target.parentNode!; - const usingVirtualParent = this.fragmentParentMap.has(parent); - - /** - * Always use existing DOM node, when it's there. - * In in-memory replay, there is virtual node, but it's `sheet` is inaccessible. - * Hence, we buffer all style changes in virtualStyleRulesMap. - */ - const styleSheet = usingVirtualParent ? null : styleEl.sheet; - let rules: VirtualStyleRules; - - if (!styleSheet) { - /** - * styleEl.sheet is only accessible if the styleEl is part of the - * dom. This doesn't work on DocumentFragments so we have to add the - * style mutations to the virtualStyleRulesMap. - */ - - if (this.virtualStyleRulesMap.has(target)) { - rules = this.virtualStyleRulesMap.get(target) as VirtualStyleRules; - } else { - rules = []; - this.virtualStyleRulesMap.set(target, rules); + if (this.usingVirtualDom) { + const target = this.virtualDom.mirror.getNode(d.id) as RRStyleElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); } - } - - if (d.adds) { - d.adds.forEach(({ rule, index: nestedIndex }) => { - if (styleSheet) { - try { - if (Array.isArray(nestedIndex)) { - const { positions, index } = getPositionsAndIndex( - nestedIndex, - ); - const nestedRule = getNestedRule( - styleSheet.cssRules, - positions, - ); - nestedRule.insertRule(rule, index); - } else { - const index = - nestedIndex === undefined - ? undefined - : Math.min(nestedIndex, styleSheet.cssRules.length); - styleSheet.insertRule(rule, index); - } - } catch (e) { - /** - * sometimes we may capture rules with browser prefix - * insert rule with prefixs in other browsers may cause Error - */ - /** - * accessing styleSheet rules may cause SecurityError - * for specific access control settings - */ + 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 }), + ); + } else { + const target = this.mirror.getNode(d.id); + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + const styleSheet = ((target as Node) as HTMLStyleElement).sheet!; + d.adds?.forEach(({ rule, index: nestedIndex }) => { + try { + if (Array.isArray(nestedIndex)) { + const { positions, index } = getPositionsAndIndex(nestedIndex); + const nestedRule = getNestedRule( + styleSheet.cssRules, + positions, + ); + nestedRule.insertRule(rule, index); + } else { + const index = + nestedIndex === undefined + ? undefined + : Math.min(nestedIndex, styleSheet.cssRules.length); + styleSheet.insertRule(rule, index); } - } else { - rules?.push({ - cssText: rule, - index: nestedIndex, - type: StyleRuleType.Insert, - }); + } catch (e) { + /** + * sometimes we may capture rules with browser prefix + * insert rule with prefixs in other browsers may cause Error + */ + /** + * accessing styleSheet rules may cause SecurityError + * for specific access control settings + */ } }); - } - if (d.removes) { - d.removes.forEach(({ index: nestedIndex }) => { - if (usingVirtualParent) { - rules?.push({ index: nestedIndex, type: StyleRuleType.Remove }); - } else { - try { - if (Array.isArray(nestedIndex)) { - const { positions, index } = getPositionsAndIndex( - nestedIndex, - ); - const nestedRule = getNestedRule( - styleSheet!.cssRules, - positions, - ); - nestedRule.deleteRule(index || 0); - } else { - styleSheet?.deleteRule(nestedIndex); - } - } catch (e) { - /** - * same as insertRule - */ + d.removes?.forEach(({ index: nestedIndex }) => { + try { + if (Array.isArray(nestedIndex)) { + const { positions, index } = getPositionsAndIndex(nestedIndex); + const nestedRule = getNestedRule( + styleSheet.cssRules, + positions, + ); + nestedRule.deleteRule(index || 0); + } else { + styleSheet?.deleteRule(nestedIndex); } + } catch (e) { + /** + * same as insertRule + */ } }); } break; } case IncrementalSource.StyleDeclaration: { - // same with StyleSheetRule - const target = this.mirror.getNode(d.id); - if (!target) { - return this.debugNodeNotFound(d, d.id); - } - - const styleEl = target as HTMLStyleElement; - const parent = target.parentNode!; - const usingVirtualParent = this.fragmentParentMap.has(parent); - - const styleSheet = usingVirtualParent ? null : styleEl.sheet; - let rules: VirtualStyleRules = []; - - if (!styleSheet) { - if (this.virtualStyleRulesMap.has(target)) { - rules = this.virtualStyleRulesMap.get(target) as VirtualStyleRules; - } else { - rules = []; - this.virtualStyleRulesMap.set(target, rules); + if (this.usingVirtualDom) { + const target = this.virtualDom.mirror.getNode(d.id) as RRStyleElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); } - } - - if (d.set) { - if (styleSheet) { - const rule = (getNestedRule( - styleSheet.rules, - d.index, - ) as unknown) as CSSStyleRule; - rule.style.setProperty(d.set.property, d.set.value, d.set.priority); - } else { + 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, + }); + } else { + const target = (this.mirror.getNode( + d.id, + ) as Node) as HTMLStyleElement; + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + const styleSheet = target.sheet!; + if (d.set) { + const rule = (getNestedRule( + styleSheet.rules, + d.index, + ) as unknown) as CSSStyleRule; + rule.style.setProperty(d.set.property, d.set.value, d.set.priority); } - } - if (d.remove) { - if (styleSheet) { + if (d.remove) { const rule = (getNestedRule( styleSheet.rules, d.index, ) as unknown) as CSSStyleRule; rule.style.removeProperty(d.remove.property); - } else { - rules.push({ - type: StyleRuleType.RemoveProperty, - index: d.index, - ...d.remove, - }); } } break; @@ -1305,20 +1309,31 @@ export class Replayer { if (!this.config.UNSAFE_replayCanvas) { return; } - const target = this.mirror.getNode(d.id); - if (!target) { - return this.debugNodeNotFound(d, d.id); + if (this.usingVirtualDom) { + 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, + }); + } else { + const target = this.mirror.getNode(d.id); + if (!target) { + return this.debugNodeNotFound(d, d.id); + } + canvasMutation({ + event: e, + mutation: d, + target: target as HTMLCanvasElement, + imageMap: this.imageMap, + canvasEventMap: this.canvasEventMap, + errorHandler: this.warnCanvasMutationFailed.bind(this), + }); } - - canvasMutation({ - event: e, - mutation: d, - target: target as HTMLCanvasElement, - imageMap: this.imageMap, - canvasEventMap: this.canvasEventMap, - errorHandler: this.warnCanvasMutationFailed.bind(this), - }); - break; } case IncrementalSource.Font: { @@ -1340,9 +1355,35 @@ export class Replayer { } } - private applyMutation(d: mutationData, useVirtualParent: boolean) { + private applyMutation(d: mutationData, isSync: boolean) { + // Only apply virtual dom optimization if the fast-forward process has node mutation. Because the cost of creating a virtual dom tree and executing the diff algorithm is usually higher than directly applying other kind of events. + if (this.config.useVirtualDom && !this.usingVirtualDom && isSync) { + this.usingVirtualDom = true; + buildFromDom(this.iframe.contentDocument!, this.mirror, this.virtualDom); + // If these legacy missing nodes haven't been resolved, they should be converted to virtual nodes. + if (Object.keys(this.legacy_missingNodeRetryMap).length) { + for (const key in this.legacy_missingNodeRetryMap) { + try { + const value = this.legacy_missingNodeRetryMap[key]; + const virtualNode = buildFromNode( + value.node as Node, + this.virtualDom, + this.mirror, + ); + if (virtualNode) value.node = virtualNode; + } catch (error) { + if (this.config.showWarning) { + console.warn(error); + } + } + } + } + } + const mirror = this.usingVirtualDom ? this.virtualDom.mirror : this.mirror; + type TNode = typeof mirror extends Mirror ? Node : RRNode; + d.removes.forEach((mutation) => { - let target = this.mirror.getNode(mutation.id); + let target = mirror.getNode(mutation.id); if (!target) { if (d.removes.find((r) => r.id === mutation.parentId)) { // no need to warn, parent was already removed @@ -1350,53 +1391,43 @@ export class Replayer { } return this.warnNodeNotFound(d, mutation.id); } - if (this.virtualStyleRulesMap.has(target)) { - this.virtualStyleRulesMap.delete(target); - } - let parent: Node | null | ShadowRoot = this.mirror.getNode( + let parent: Node | null | ShadowRoot | RRNode = mirror.getNode( mutation.parentId, ); if (!parent) { return this.warnNodeNotFound(d, mutation.parentId); } - if (mutation.isShadow && hasShadowRoot(parent)) { - parent = parent.shadowRoot; + if (mutation.isShadow && hasShadowRoot(parent as Node)) { + parent = (parent as Element | RRElement).shadowRoot; } // target may be removed with its parents before - this.mirror.removeNodeFromMap(target); - if (parent) { - let realTarget = null; - const realParent = this.mirror.getMeta(parent) - ? this.fragmentParentMap.get(parent) - : undefined; - if (realParent && realParent.contains(target)) { - parent = realParent; - } else if (this.fragmentParentMap.has(target)) { + mirror.removeNodeFromMap(target as Node & RRNode); + if (parent) + try { + parent.removeChild(target as Node & RRNode); /** - * the target itself is a fragment document and it's not in the dom - * so we should remove the real target from its parent + * https://github.com/rrweb-io/rrweb/pull/887 + * Remove any virtual style rules for stylesheets if a child text node is removed. */ - realTarget = this.fragmentParentMap.get(target)!; - this.fragmentParentMap.delete(target); - target = realTarget; - } - try { - parent.removeChild(target); + if ( + this.usingVirtualDom && + 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, - realParent, target, - realTarget, d, ); } else { throw error; } } - } }); // tslint:disable-next-line: variable-name @@ -1407,9 +1438,9 @@ export class Replayer { // next not present at this moment const nextNotInDOM = (mutation: addedNodeMutation) => { - let next: Node | null = null; + let next: TNode | null = null; if (mutation.nextId) { - next = this.mirror.getNode(mutation.nextId); + next = mirror.getNode(mutation.nextId) as TNode | null; } // next not present at this moment if ( @@ -1427,7 +1458,7 @@ export class Replayer { if (!this.iframe.contentDocument) { return console.warn('Looks like your replayer has been destroyed.'); } - let parent: Node | null | ShadowRoot = this.mirror.getNode( + let parent: Node | null | ShadowRoot | RRNode = mirror.getNode( mutation.parentId, ); if (!parent) { @@ -1438,78 +1469,49 @@ export class Replayer { return queue.push(mutation); } - let parentInDocument = null; - if (this.iframe.contentDocument.contains) { - parentInDocument = this.iframe.contentDocument.contains(parent); - } else if (this.iframe.contentDocument.body.contains) { - // fix for IE - // refer 'Internet Explorer notes' at https://developer.mozilla.org/zh-CN/docs/Web/API/Document - parentInDocument = this.iframe.contentDocument.body.contains(parent); - } - - const hasIframeChild = - (parent as HTMLElement).getElementsByTagName?.('iframe').length > 0; - /** - * Why !isSerializedIframe(parent)? If parent element is an iframe, iframe document can't be appended to virtual parent. - * Why !hasIframeChild? If we move iframe elements from dom to fragment document, we will lose the contentDocument of iframe. So we need to disable the virtual dom optimization if a parent node contains iframe elements. - */ - if ( - useVirtualParent && - parentInDocument && - !isSerializedIframe(parent, this.mirror) && - !hasIframeChild - ) { - const virtualParent = document.createDocumentFragment(); - this.mirror.replace(mutation.parentId, virtualParent); - this.fragmentParentMap.set(virtualParent, parent); - - // store the state, like scroll position, of child nodes before they are unmounted from dom - this.storeState(parent); - - while (parent.firstChild) { - virtualParent.appendChild(parent.firstChild); - } - parent = virtualParent; - } - 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)) { - (parent as HTMLElement).attachShadow({ mode: 'open' }); - parent = (parent as HTMLElement).shadowRoot!; - } else parent = parent.shadowRoot; + (parent as Element | RRElement).attachShadow({ mode: 'open' }); + parent = (parent as Element | RRElement).shadowRoot! as Node | RRNode; + } else parent = parent.shadowRoot as Node | RRNode; } - let previous: Node | null = null; - let next: Node | null = null; + let previous: Node | RRNode | null = null; + let next: Node | RRNode | null = null; if (mutation.previousId) { - previous = this.mirror.getNode(mutation.previousId); + previous = mirror.getNode(mutation.previousId); } if (mutation.nextId) { - next = this.mirror.getNode(mutation.nextId); + next = mirror.getNode(mutation.nextId); } if (nextNotInDOM(mutation)) { return queue.push(mutation); } - if (mutation.node.rootId && !this.mirror.getNode(mutation.node.rootId)) { + if (mutation.node.rootId && !mirror.getNode(mutation.node.rootId)) { return; } const targetDoc = mutation.node.rootId - ? this.mirror.getNode(mutation.node.rootId) + ? mirror.getNode(mutation.node.rootId) + : this.usingVirtualDom + ? this.virtualDom : this.iframe.contentDocument; - if (isSerializedIframe(parent, this.mirror)) { - this.attachDocumentToIframe(mutation, parent); + if (isSerializedIframe(parent, mirror)) { + this.attachDocumentToIframe( + mutation, + parent as HTMLIFrameElement | RRIFrameElement, + ); return; } const target = buildNodeWithSN(mutation.node, { - doc: targetDoc as Document, - mirror: this.mirror, + doc: targetDoc as Document, // can be Document or RRDocument + mirror: mirror as Mirror, // can be this.mirror or virtualDom.mirror skipChild: true, hackCss: true, cache: this.cache, - })!; + }) as Node | RRNode; // legacy data, we should not have -1 siblings any more if (mutation.previousId === -1 || mutation.nextId === -1) { @@ -1520,50 +1522,76 @@ export class Replayer { return; } - const parentSn = this.mirror.getMeta(parent); + // Typescripts type system is not smart enough + // to understand what is going on with the types below + type TNode = typeof mirror extends Mirror ? Node : RRNode; + type TMirror = typeof mirror extends Mirror ? Mirror : RRDOMMirror; + + const parentSn = (mirror as TMirror).getMeta(parent as TNode); if ( parentSn && parentSn.type === NodeType.Element && parentSn.tagName === 'textarea' && mutation.node.type === NodeType.Text ) { + const childNodeArray = Array.isArray(parent.childNodes) + ? parent.childNodes + : Array.from(parent.childNodes); + // 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 childNodeArray) { if (c.nodeType === parent.TEXT_NODE) { - parent.removeChild(c); + parent.removeChild(c as Node & RRNode); } } } if (previous && previous.nextSibling && previous.nextSibling.parentNode) { - parent.insertBefore(target, previous.nextSibling); + (parent as TNode).insertBefore( + target as TNode, + previous.nextSibling as TNode, + ); } 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); + (parent as TNode).contains(next as TNode) + ? (parent as TNode).insertBefore(target as TNode, next as TNode) + : (parent as TNode).insertBefore(target as TNode, 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); + (targetDoc as TNode).removeChild(targetDoc.firstChild as TNode); } } - parent.appendChild(target); + (parent as TNode).appendChild(target as TNode); } + /** + * https://github.com/rrweb-io/rrweb/pull/887 + * Remove any virtual style rules for stylesheets if a new text node is appended. + */ + if ( + this.usingVirtualDom && + target.nodeName === '#text' && + parent.nodeName === 'STYLE' && + (parent as RRStyleElement).rules?.length > 0 + ) + (parent as RRStyleElement).rules = []; if (isSerializedIframe(target, this.mirror)) { - const targetId = this.mirror.getId(target); + const targetId = this.mirror.getId(target as HTMLIFrameElement); const mutationInQueue = this.newDocumentQueue.find( (m) => m.parentId === targetId, ); if (mutationInQueue) { - this.attachDocumentToIframe(mutationInQueue, target); + this.attachDocumentToIframe( + mutationInQueue, + target as HTMLIFrameElement, + ); this.newDocumentQueue = this.newDocumentQueue.filter( (m) => m !== mutationInQueue, ); @@ -1597,7 +1625,7 @@ export class Replayer { break; } for (const tree of resolveTrees) { - let parent = this.mirror.getNode(tree.value.parentId); + let parent = mirror.getNode(tree.value.parentId); if (!parent) { this.debug( 'Drop resolve tree since there is no parent for the root node.', @@ -1616,7 +1644,7 @@ export class Replayer { } uniqueTextMutations(d.texts).forEach((mutation) => { - let target = this.mirror.getNode(mutation.id); + let target = mirror.getNode(mutation.id); if (!target) { if (d.removes.find((r) => r.id === mutation.id)) { // no need to warn, element was already removed @@ -1624,16 +1652,19 @@ export class Replayer { } return this.warnNodeNotFound(d, mutation.id); } + target.textContent = mutation.value; + /** - * apply text content to real parent directly + * https://github.com/rrweb-io/rrweb/pull/865 + * Remove any virtual style rules for stylesheets whose contents are replaced. */ - if (this.fragmentParentMap.has(target)) { - target = this.fragmentParentMap.get(target)!; + if (this.usingVirtualDom) { + const parent = target.parentNode as RRStyleElement; + if (parent?.rules?.length > 0) parent.rules = []; } - target.textContent = mutation.value; }); d.attributes.forEach((mutation) => { - let target = this.mirror.getNode(mutation.id); + let target = mirror.getNode(mutation.id); if (!target) { if (d.removes.find((r) => r.id === mutation.id)) { // no need to warn, element was already removed @@ -1641,17 +1672,17 @@ export class Replayer { } return this.warnNodeNotFound(d, mutation.id); } - if (this.fragmentParentMap.has(target)) { - target = this.fragmentParentMap.get(target)!; - } for (const attributeName in mutation.attributes) { if (typeof attributeName === 'string') { const value = mutation.attributes[attributeName]; if (value === null) { - (target as Element).removeAttribute(attributeName); + (target as Element | RRElement).removeAttribute(attributeName); } else if (typeof value === 'string') { try { - (target as Element).setAttribute(attributeName, value); + (target as Element | RRElement).setAttribute( + attributeName, + value, + ); } catch (error) { if (this.config.showWarning) { console.warn( @@ -1662,7 +1693,7 @@ export class Replayer { } } else if (attributeName === 'style') { let styleValues = value as styleAttributeValue; - const targetEl = target as HTMLElement; + const targetEl = target as HTMLElement | RRElement; for (var s in styleValues) { if (styleValues[s] === false) { targetEl.style.removeProperty(s); @@ -1731,22 +1762,10 @@ export class Replayer { } } - private applyText(d: textMutation, mutation: mutationData) { - const target = this.mirror.getNode(d.id); - if (!target) { - return this.debugNodeNotFound(mutation, d.id); - } - try { - (target as HTMLElement).textContent = d.value; - } catch (error) { - // for safe - } - } - private legacy_resolveMissingNode( map: missingNodeMap, - parent: Node, - target: Node, + parent: Node | RRNode, + target: Node | RRNode, targetMutation: addedNodeMutation, ) { const { previousId, nextId } = targetMutation; @@ -1754,7 +1773,7 @@ export class Replayer { const nextInMap = nextId && map[nextId]; if (previousInMap) { const { node, mutation } = previousInMap as missingNode; - parent.insertBefore(node, target); + parent.insertBefore(node as Node & RRNode, target as Node & RRNode); delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; if (mutation.previousId || mutation.nextId) { @@ -1763,7 +1782,10 @@ export class Replayer { } if (nextInMap) { const { node, mutation } = nextInMap as missingNode; - parent.insertBefore(node, target.nextSibling); + parent.insertBefore( + node as Node & RRNode, + target.nextSibling as Node & RRNode, + ); delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; if (mutation.previousId || mutation.nextId) { @@ -1868,104 +1890,8 @@ export class Replayer { }); } - /** - * Replace the virtual parent with the real parent. - * @param frag fragment document, the virtual parent - * @param parent real parent element - */ - private restoreRealParent(frag: Node, parent: Node) { - const id = this.mirror.getId(frag); - const parentSn = this.mirror.getMeta(parent); - this.mirror.replace(id, parent); - - /** - * If we have already set value attribute on textarea, - * then we could not apply text content as default value any more. - */ - if ( - parentSn?.type === NodeType.Element && - parentSn?.tagName === 'textarea' && - frag.textContent - ) { - (parent as HTMLTextAreaElement).value = frag.textContent; - } - parent.appendChild(frag); - // restore state of elements after they are mounted - this.restoreState(parent); - } - - /** - * store state of elements before unmounted from dom recursively - * the state should be restored in the handler of event ReplayerEvents.Flush - * e.g. browser would lose scroll position after the process that we add children of parent node to Fragment Document as virtual dom - */ - private storeState(parent: Node) { - if (parent) { - if (parent.nodeType === parent.ELEMENT_NODE) { - const parentElement = parent as HTMLElement; - if (parentElement.scrollLeft || parentElement.scrollTop) { - // store scroll position state - this.elementStateMap.set(parent, { - scroll: [parentElement.scrollLeft, parentElement.scrollTop], - }); - } - if (parentElement.tagName === 'STYLE') - storeCSSRules( - parentElement as HTMLStyleElement, - this.virtualStyleRulesMap, - ); - const children = parentElement.children; - for (const child of Array.from(children)) { - this.storeState(child); - } - } - } - } - - /** - * restore the state of elements recursively, which was stored before elements were unmounted from dom in virtual parent mode - * this function corresponds to function storeState - */ - private restoreState(parent: Node) { - if (parent.nodeType === parent.ELEMENT_NODE) { - const parentElement = parent as HTMLElement; - if (this.elementStateMap.has(parent)) { - const storedState = this.elementStateMap.get(parent)!; - // restore scroll position - if (storedState.scroll) { - parentElement.scrollLeft = storedState.scroll[0]; - parentElement.scrollTop = storedState.scroll[1]; - } - this.elementStateMap.delete(parent); - } - const children = parentElement.children; - for (const child of Array.from(children)) { - this.restoreState(child); - } - } - } - - private restoreNodeSheet(node: Node) { - const storedRules = this.virtualStyleRulesMap.get(node); - if (node.nodeName !== 'STYLE') { - return; - } - - if (!storedRules) { - return; - } - - const styleNode = node as HTMLStyleElement; - - applyVirtualStyleRulesToNode(storedRules, styleNode); - } - private warnNodeNotFound(d: incrementalData, id: number) { - if (this.treeIndex.idRemoved(id)) { - this.warn(`Node with id '${id}' was previously removed. `, d); - } else { - this.warn(`Node with id '${id}' not found. `, d); - } + this.warn(`Node with id '${id}' not found. `, d); } private warnCanvasMutationFailed( @@ -1982,15 +1908,7 @@ export class Replayer { * is microtask, so events fired on a removed DOM may emit * snapshots in the reverse order. */ - if (this.treeIndex.idRemoved(id)) { - this.debug( - REPLAY_CONSOLE_PREFIX, - `Node with id '${id}' was previously removed. `, - d, - ); - } else { - this.debug(REPLAY_CONSOLE_PREFIX, `Node with id '${id}' not found. `, d); - } + this.debug(REPLAY_CONSOLE_PREFIX, `Node with id '${id}' not found. `, d); } private warn(...args: Parameters) { diff --git a/packages/rrweb/src/replay/virtual-styles.ts b/packages/rrweb/src/replay/virtual-styles.ts deleted file mode 100644 index ca13344af2..0000000000 --- a/packages/rrweb/src/replay/virtual-styles.ts +++ /dev/null @@ -1,186 +0,0 @@ -export enum StyleRuleType { - Insert, - Remove, - Snapshot, - SetProperty, - RemoveProperty, -} - -type InsertRule = { - cssText: string; - type: StyleRuleType.Insert; - index?: number | number[]; -}; -type RemoveRule = { - type: StyleRuleType.Remove; - index: number | number[]; -}; -type SnapshotRule = { - type: StyleRuleType.Snapshot; - cssTexts: string[]; -}; -type SetPropertyRule = { - type: StyleRuleType.SetProperty; - index: number[]; - property: string; - value: string | null; - priority: string | undefined; -}; -type RemovePropertyRule = { - type: StyleRuleType.RemoveProperty; - index: number[]; - property: string; -}; - -export type VirtualStyleRules = Array< - InsertRule | RemoveRule | SnapshotRule | SetPropertyRule | RemovePropertyRule ->; -export type VirtualStyleRulesMap = Map; - -export function getNestedRule( - rules: CSSRuleList, - position: number[], -): CSSGroupingRule { - const rule = rules[position[0]] as CSSGroupingRule; - if (position.length === 1) { - return rule; - } else { - return getNestedRule( - ((rule as CSSGroupingRule).cssRules[position[1]] as CSSGroupingRule) - .cssRules, - position.slice(2), - ); - } -} - -export function getPositionsAndIndex(nestedIndex: number[]) { - const positions = [...nestedIndex]; - const index = positions.pop(); - return { positions, index }; -} - -export function applyVirtualStyleRulesToNode( - storedRules: VirtualStyleRules, - styleNode: HTMLStyleElement, -) { - const { sheet } = styleNode; - if (!sheet) { - // styleNode without sheet means the DOM has been removed - // so the rules no longer need to be applied - return; - } - - storedRules.forEach((rule) => { - if (rule.type === StyleRuleType.Insert) { - try { - if (Array.isArray(rule.index)) { - const { positions, index } = getPositionsAndIndex(rule.index); - const nestedRule = getNestedRule(sheet.cssRules, positions); - nestedRule.insertRule(rule.cssText, index); - } else { - sheet.insertRule(rule.cssText, rule.index); - } - } catch (e) { - /** - * sometimes we may capture rules with browser prefix - * insert rule with prefixs in other browsers may cause Error - */ - } - } else if (rule.type === StyleRuleType.Remove) { - try { - if (Array.isArray(rule.index)) { - const { positions, index } = getPositionsAndIndex(rule.index); - const nestedRule = getNestedRule(sheet.cssRules, positions); - nestedRule.deleteRule(index || 0); - } else { - sheet.deleteRule(rule.index); - } - } catch (e) { - /** - * accessing styleSheet rules may cause SecurityError - * for specific access control settings - */ - } - } else if (rule.type === StyleRuleType.Snapshot) { - restoreSnapshotOfStyleRulesToNode(rule.cssTexts, styleNode); - } else if (rule.type === StyleRuleType.SetProperty) { - const nativeRule = (getNestedRule( - sheet.cssRules, - rule.index, - ) as unknown) as CSSStyleRule; - nativeRule.style.setProperty(rule.property, rule.value, rule.priority); - } else if (rule.type === StyleRuleType.RemoveProperty) { - const nativeRule = (getNestedRule( - sheet.cssRules, - rule.index, - ) as unknown) as CSSStyleRule; - nativeRule.style.removeProperty(rule.property); - } - }); -} - -function restoreSnapshotOfStyleRulesToNode( - cssTexts: string[], - styleNode: HTMLStyleElement, -) { - try { - const existingRules = Array.from(styleNode.sheet?.cssRules || []).map( - (rule) => rule.cssText, - ); - const existingRulesReversed = Object.entries(existingRules).reverse(); - let lastMatch = existingRules.length; - existingRulesReversed.forEach(([index, rule]) => { - const indexOf = cssTexts.indexOf(rule); - if (indexOf === -1 || indexOf > lastMatch) { - try { - styleNode.sheet?.deleteRule(Number(index)); - } catch (e) { - /** - * accessing styleSheet rules may cause SecurityError - * for specific access control settings - */ - } - } - lastMatch = indexOf; - }); - cssTexts.forEach((cssText, index) => { - try { - if (styleNode.sheet?.cssRules[index]?.cssText !== cssText) { - styleNode.sheet?.insertRule(cssText, index); - } - } catch (e) { - /** - * sometimes we may capture rules with browser prefix - * insert rule with prefixs in other browsers may cause Error - */ - } - }); - } catch (e) { - /** - * accessing styleSheet rules may cause SecurityError - * for specific access control settings - */ - } -} - -export function storeCSSRules( - parentElement: HTMLStyleElement, - virtualStyleRulesMap: VirtualStyleRulesMap, -) { - try { - const cssTexts = Array.from( - (parentElement as HTMLStyleElement).sheet?.cssRules || [], - ).map((rule) => rule.cssText); - virtualStyleRulesMap.set(parentElement, [ - { - type: StyleRuleType.Snapshot, - cssTexts, - }, - ]); - } catch (e) { - /** - * accessing styleSheet rules may cause SecurityError - * for specific access control settings - */ - } -} diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 629df4baa5..a74a091c75 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -1,4 +1,4 @@ -import { +import type { serializedNodeWithId, Mirror, INode, @@ -7,11 +7,12 @@ import { MaskInputFn, MaskTextFn, } from 'rrweb-snapshot'; -import { PackFn, UnpackFn } from './packer/base'; -import { IframeManager } from './record/iframe-manager'; -import { ShadowDomManager } from './record/shadow-dom-manager'; +import type { PackFn, UnpackFn } from './packer/base'; +import type { IframeManager } from './record/iframe-manager'; +import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import { CanvasManager } from './record/observers/canvas/canvas-manager'; +import type { RRNode } from 'rrdom/es/virtual-dom'; +import type { CanvasManager } from './record/observers/canvas/canvas-manager'; export enum EventType { DomContentLoaded, @@ -169,6 +170,11 @@ export type eventWithTime = event & { delay?: number; }; +export type canvasEventWithTime = eventWithTime & { + type: EventType.IncrementalSnapshot; + data: canvasMutationData; +}; + export type blockClass = string | RegExp; export type maskTextClass = string | RegExp; @@ -653,6 +659,7 @@ export type playerConfig = { strokeStyle?: string; }; unpackFn?: UnpackFn; + useVirtualDom: boolean; plugins?: ReplayPlugin[]; }; @@ -663,7 +670,7 @@ export type playerMetaData = { }; export type missingNode = { - node: Node; + node: Node | RRNode; mutation: addedNodeMutation; }; export type missingNodeMap = { @@ -706,12 +713,6 @@ export enum ReplayerEvents { PlayBack = 'play-back', } -// store the state that would be changed during the process(unmount from dom and mount again) -export type ElementState = { - // [scrollLeft,scrollTop] - scroll?: [number, number]; -}; - export type KeepIframeSrcFn = (src: string) => boolean; declare global { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 6a556f4aac..5a1081ec52 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -1,21 +1,17 @@ -import { +import type { throttleOptions, listenerHandler, hookResetter, blockClass, - IncrementalSource, addedNodeMutation, - removedNodeMutation, - textMutation, - attributeMutation, - mutationData, - scrollData, - inputData, DocumentDimension, IWindow, DeprecatedMirror, + textMutation, } from './types'; -import { Mirror, IGNORED_NODE, isShadowRoot } from 'rrweb-snapshot'; +import type { IMirror, Mirror } from 'rrweb-snapshot'; +import { isShadowRoot, IGNORED_NODE } from 'rrweb-snapshot'; +import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom'; export function on( type: string, @@ -280,201 +276,6 @@ export function polyfill(win = window) { } } -export type TreeNode = { - id: number; - mutation: addedNodeMutation; - parent?: TreeNode; - children: Record; - texts: textMutation[]; - attributes: attributeMutation[]; -}; - -export class TreeIndex { - public tree!: Record; - - private removeNodeMutations!: removedNodeMutation[]; - private textMutations!: textMutation[]; - private attributeMutations!: attributeMutation[]; - private indexes!: Map; - private removeIdSet!: Set; - private scrollMap!: Map; - private inputMap!: Map; - - constructor() { - this.reset(); - } - - public add(mutation: addedNodeMutation) { - const parentTreeNode = this.indexes.get(mutation.parentId); - const treeNode: TreeNode = { - id: mutation.node.id, - mutation, - children: [], - texts: [], - attributes: [], - }; - if (!parentTreeNode) { - this.tree[treeNode.id] = treeNode; - } else { - treeNode.parent = parentTreeNode; - parentTreeNode.children[treeNode.id] = treeNode; - } - this.indexes.set(treeNode.id, treeNode); - } - - public remove(mutation: removedNodeMutation, mirror: Mirror) { - const parentTreeNode = this.indexes.get(mutation.parentId); - const treeNode = this.indexes.get(mutation.id); - - const deepRemoveFromMirror = (id: number) => { - if (id === -1) return; - - this.removeIdSet.add(id); - const node = mirror.getNode(id); - node?.childNodes.forEach((childNode) => { - deepRemoveFromMirror(mirror.getId(childNode)); - }); - }; - const deepRemoveFromTreeIndex = (node: TreeNode) => { - this.removeIdSet.add(node.id); - Object.values(node.children).forEach((n) => deepRemoveFromTreeIndex(n)); - const _treeNode = this.indexes.get(node.id); - if (_treeNode) { - const _parentTreeNode = _treeNode.parent; - if (_parentTreeNode) { - delete _treeNode.parent; - delete _parentTreeNode.children[_treeNode.id]; - this.indexes.delete(mutation.id); - } - } - }; - - if (!treeNode) { - this.removeNodeMutations.push(mutation); - deepRemoveFromMirror(mutation.id); - } else if (!parentTreeNode) { - delete this.tree[treeNode.id]; - this.indexes.delete(treeNode.id); - deepRemoveFromTreeIndex(treeNode); - } else { - delete treeNode.parent; - delete parentTreeNode.children[treeNode.id]; - this.indexes.delete(mutation.id); - deepRemoveFromTreeIndex(treeNode); - } - } - - public text(mutation: textMutation) { - const treeNode = this.indexes.get(mutation.id); - if (treeNode) { - treeNode.texts.push(mutation); - } else { - this.textMutations.push(mutation); - } - } - - public attribute(mutation: attributeMutation) { - const treeNode = this.indexes.get(mutation.id); - if (treeNode) { - treeNode.attributes.push(mutation); - } else { - this.attributeMutations.push(mutation); - } - } - - public scroll(d: scrollData) { - this.scrollMap.set(d.id, d); - } - - public input(d: inputData) { - this.inputMap.set(d.id, d); - } - - public flush(): { - mutationData: mutationData; - scrollMap: TreeIndex['scrollMap']; - inputMap: TreeIndex['inputMap']; - } { - const { - tree, - removeNodeMutations, - textMutations, - attributeMutations, - } = this; - - const batchMutationData: mutationData = { - source: IncrementalSource.Mutation, - removes: removeNodeMutations, - texts: textMutations, - attributes: attributeMutations, - adds: [], - }; - - const walk = (treeNode: TreeNode, removed: boolean) => { - if (removed) { - this.removeIdSet.add(treeNode.id); - } - batchMutationData.texts = batchMutationData.texts - .concat(removed ? [] : treeNode.texts) - .filter((m) => !this.removeIdSet.has(m.id)); - batchMutationData.attributes = batchMutationData.attributes - .concat(removed ? [] : treeNode.attributes) - .filter((m) => !this.removeIdSet.has(m.id)); - if ( - !this.removeIdSet.has(treeNode.id) && - !this.removeIdSet.has(treeNode.mutation.parentId) && - !removed - ) { - batchMutationData.adds.push(treeNode.mutation); - if (treeNode.children) { - Object.values(treeNode.children).forEach((n) => walk(n, false)); - } - } else { - Object.values(treeNode.children).forEach((n) => walk(n, true)); - } - }; - - Object.values(tree).forEach((n) => walk(n, false)); - - for (const id of this.scrollMap.keys()) { - if (this.removeIdSet.has(id)) { - this.scrollMap.delete(id); - } - } - for (const id of this.inputMap.keys()) { - if (this.removeIdSet.has(id)) { - this.inputMap.delete(id); - } - } - - const scrollMap = new Map(this.scrollMap); - const inputMap = new Map(this.inputMap); - - this.reset(); - - return { - mutationData: batchMutationData, - scrollMap, - inputMap, - }; - } - - private reset() { - this.tree = []; - this.indexes = new Map(); - this.removeNodeMutations = []; - this.textMutations = []; - this.attributeMutations = []; - this.removeIdSet = new Set(); - this.scrollMap = new Map(); - this.inputMap = new Map(); - } - - public idRemoved(id: number): boolean { - return this.removeIdSet.has(id); - } -} - type ResolveTree = { value: addedNodeMutation; children: ResolveTree[]; @@ -542,13 +343,13 @@ export function iterateResolveTree( export type AppendedIframe = { mutationInQueue: addedNodeMutation; - builtNode: HTMLIFrameElement; + builtNode: HTMLIFrameElement | RRIFrameElement; }; -export function isSerializedIframe( - n: Node, - mirror: Mirror, -): n is HTMLIFrameElement { +export function isSerializedIframe( + n: TNode, + mirror: IMirror, +): boolean { return Boolean(n.nodeName === 'IFRAME' && mirror.getMeta(n)); } @@ -582,12 +383,34 @@ export function getBaseDimension( }; } -export function hasShadowRoot( +export function hasShadowRoot( n: T, ): n is T & { shadowRoot: ShadowRoot } { return Boolean(((n as unknown) as Element)?.shadowRoot); } +export function getNestedRule( + rules: CSSRuleList, + position: number[], +): CSSGroupingRule { + const rule = rules[position[0]] as CSSGroupingRule; + if (position.length === 1) { + return rule; + } else { + return getNestedRule( + ((rule as CSSGroupingRule).cssRules[position[1]] as CSSGroupingRule) + .cssRules, + position.slice(2), + ); + } +} + +export function getPositionsAndIndex(nestedIndex: number[]) { + const positions = [...nestedIndex]; + const index = positions.pop(); + return { positions, index }; +} + /** * Returns the latest mutation in the queue for each node. * @param {textMutation[]} mutations The text mutations to filter. diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 9a4fa49a15..5af8b6dcf1 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -8362,7 +8362,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"assert\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:2:37\\" + \\"__puppeteer_evaluation_script__:2:21\\" ], \\"payload\\": [ \\"true\\", @@ -8378,7 +8378,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"count\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:3:37\\" + \\"__puppeteer_evaluation_script__:3:21\\" ], \\"payload\\": [ \\"\\\\\\"count\\\\\\"\\" @@ -8393,7 +8393,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"countReset\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:4:37\\" + \\"__puppeteer_evaluation_script__:4:21\\" ], \\"payload\\": [ \\"\\\\\\"count\\\\\\"\\" @@ -8408,7 +8408,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"debug\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:5:37\\" + \\"__puppeteer_evaluation_script__:5:21\\" ], \\"payload\\": [ \\"\\\\\\"debug\\\\\\"\\" @@ -8423,7 +8423,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"dir\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:6:37\\" + \\"__puppeteer_evaluation_script__:6:21\\" ], \\"payload\\": [ \\"\\\\\\"dir\\\\\\"\\" @@ -8438,7 +8438,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"dirxml\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:7:37\\" + \\"__puppeteer_evaluation_script__:7:21\\" ], \\"payload\\": [ \\"\\\\\\"dirxml\\\\\\"\\" @@ -8453,7 +8453,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"group\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:8:37\\" + \\"__puppeteer_evaluation_script__:8:21\\" ], \\"payload\\": [] } @@ -8466,7 +8466,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"groupCollapsed\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:9:37\\" + \\"__puppeteer_evaluation_script__:9:21\\" ], \\"payload\\": [] } @@ -8479,7 +8479,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"info\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:10:37\\" + \\"__puppeteer_evaluation_script__:10:21\\" ], \\"payload\\": [ \\"\\\\\\"info\\\\\\"\\" @@ -8494,7 +8494,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"log\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:11:37\\" + \\"__puppeteer_evaluation_script__:11:21\\" ], \\"payload\\": [ \\"\\\\\\"log\\\\\\"\\" @@ -8509,7 +8509,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"table\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:12:37\\" + \\"__puppeteer_evaluation_script__:12:21\\" ], \\"payload\\": [ \\"\\\\\\"table\\\\\\"\\" @@ -8524,7 +8524,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"time\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:13:37\\" + \\"__puppeteer_evaluation_script__:13:21\\" ], \\"payload\\": [] } @@ -8537,7 +8537,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"timeEnd\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:14:37\\" + \\"__puppeteer_evaluation_script__:14:21\\" ], \\"payload\\": [] } @@ -8550,7 +8550,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"timeLog\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:15:37\\" + \\"__puppeteer_evaluation_script__:15:21\\" ], \\"payload\\": [] } @@ -8563,7 +8563,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"trace\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:16:37\\" + \\"__puppeteer_evaluation_script__:16:21\\" ], \\"payload\\": [ \\"\\\\\\"trace\\\\\\"\\" @@ -8578,7 +8578,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"warn\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:17:37\\" + \\"__puppeteer_evaluation_script__:17:21\\" ], \\"payload\\": [ \\"\\\\\\"warn\\\\\\"\\" @@ -8593,7 +8593,7 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"clear\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:18:37\\" + \\"__puppeteer_evaluation_script__:18:21\\" ], \\"payload\\": [] } @@ -8606,10 +8606,10 @@ exports[`record integration tests should record console messages 1`] = ` \\"payload\\": { \\"level\\": \\"log\\", \\"trace\\": [ - \\"__puppeteer_evaluation_script__:19:37\\" + \\"__puppeteer_evaluation_script__:19:21\\" ], \\"payload\\": [ - \\"\\\\\\"TypeError: a message\\\\\\\\n at __puppeteer_evaluation_script__:19:41\\\\\\\\nEnd of stack for Error object\\\\\\"\\" + \\"\\\\\\"TypeError: a message\\\\\\\\n at __puppeteer_evaluation_script__:19:25\\\\\\\\nEnd of stack for Error object\\\\\\"\\" ] } } diff --git a/packages/rrweb/test/__snapshots__/replayer.test.ts.snap b/packages/rrweb/test/__snapshots__/replayer.test.ts.snap index c471f9d958..45f9b9e3d4 100644 --- a/packages/rrweb/test/__snapshots__/replayer.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/replayer.test.ts.snap @@ -56,7 +56,7 @@ html.rrweb-paused *, html.rrweb-paused ::before, html.rrweb-paused ::after { ani file-cid-1 @charset \\"utf-8\\"; -.css-added-at-500 { padding: 1.3125rem; flex: 0 0 auto; width: 100%; } +.css-added-at-400 { padding: 1.3125rem; flex: 0 0 auto; width: 100%; } file-cid-2 @@ -64,7 +64,7 @@ file-cid-2 .css-added-at-200-overwritten-at-3000 { opacity: 1; transform: translateX(0px); } -.css-added-at-400-overwritten-at-3000 { border: 1px solid blue; } +.css-added-at-500-overwritten-at-3000 { border: 1px solid blue; } file-cid-3 diff --git a/packages/rrweb/test/e2e/webgl.test.ts b/packages/rrweb/test/e2e/webgl.test.ts index 38c4abc880..f77c92b193 100644 --- a/packages/rrweb/test/e2e/webgl.test.ts +++ b/packages/rrweb/test/e2e/webgl.test.ts @@ -1,7 +1,7 @@ -import * as http from 'http'; +import type * as http from 'http'; import * as fs from 'fs'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import type * as puppeteer from 'puppeteer'; import { startServer, launchPuppeteer, @@ -9,12 +9,7 @@ import { replaceLast, waitForRAF, } from '../utils'; -import { - recordOptions, - eventWithTime, - EventType, - IncrementalSource, -} from '../../src/types'; +import type { recordOptions, eventWithTime } from '../../src/types'; import { toMatchImageSnapshot } from 'jest-image-snapshot'; expect.extend({ toMatchImageSnapshot }); diff --git a/packages/rrweb/test/events/iframe.ts b/packages/rrweb/test/events/iframe.ts index d4110d2070..bfa75822b8 100644 --- a/packages/rrweb/test/events/iframe.ts +++ b/packages/rrweb/test/events/iframe.ts @@ -486,6 +486,30 @@ const events: eventWithTime[] = [ timestamp: now + 1500, }, // add iframe five + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 75, + nextId: null, + node: { + type: 2, + tagName: 'iframe', + attributes: { id: 'five' }, + childNodes: [], + rootId: 62, + id: 80, + }, + }, + ], + }, + timestamp: now + 2000, + }, { type: EventType.IncrementalSnapshot, data: { @@ -550,30 +574,6 @@ const events: eventWithTime[] = [ }, timestamp: now + 2000, }, - { - type: EventType.IncrementalSnapshot, - data: { - source: IncrementalSource.Mutation, - texts: [], - attributes: [], - removes: [], - adds: [ - { - parentId: 75, - nextId: null, - node: { - type: 2, - tagName: 'iframe', - attributes: { id: 'five' }, - childNodes: [], - rootId: 62, - id: 80, - }, - }, - ], - }, - timestamp: now + 2000, - }, // remove the html element of iframe four { type: EventType.IncrementalSnapshot, diff --git a/packages/rrweb/test/events/input.ts b/packages/rrweb/test/events/input.ts new file mode 100644 index 0000000000..3820f96533 --- /dev/null +++ b/packages/rrweb/test/events/input.ts @@ -0,0 +1,215 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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: 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: [], + }, + ], + }, + ], + }, + 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', + childNodes: [], + id: 26, + }, + }, + { + parentId: 26, + nextId: null, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueC' }, + childNodes: [], + id: 27, + }, + }, + { + parentId: 27, + nextId: null, + node: { type: 3, textContent: 'C', id: 28 }, + }, + { + parentId: 26, + nextId: 27, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueB', selected: true }, + childNodes: [], + id: 29, + }, + }, + { + parentId: 26, + nextId: 29, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueA' }, + childNodes: [], + id: 30, + }, + }, + { + parentId: 30, + nextId: null, + node: { type: 3, textContent: 'A', id: 31 }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: 'B', id: 32 }, + }, + ], + }, + timestamp: now + 1000, + }, + // input event + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueA', + isChecked: false, + id: 26, + }, + timestamp: now + 1500, + }, + // input event + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueC', + isChecked: false, + id: 26, + }, + timestamp: now + 2000, + }, + // mutation that adds an input element + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 5, + nextId: null, + node: { + type: 2, + tagName: 'input', + attributes: {}, + childNodes: [], + id: 33, + }, + }, + ], + }, + timestamp: now + 2500, + }, + // an input event + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'test input', + isChecked: false, + id: 33, + }, + timestamp: now + 3000, + }, + // remove the select element + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 5, id: 26 }], + adds: [], + }, + timestamp: now + 3500, + }, + // remove the input element + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 5, id: 33 }], + adds: [], + }, + timestamp: now + 4000, + }, +]; + +export default events; diff --git a/packages/rrweb/test/events/scroll.ts b/packages/rrweb/test/events/scroll.ts new file mode 100644 index 0000000000..5069b5fe11 --- /dev/null +++ b/packages/rrweb/test/events/scroll.ts @@ -0,0 +1,128 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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: [], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // mutation that adds two div elements + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 5, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: { + id: 'container', + style: 'height: 1000px; overflow: scroll;', + }, + childNodes: [], + id: 6, + }, + }, + { + parentId: 6, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: { + id: 'block', + style: 'height: 10000px; background-color: yellow;', + }, + childNodes: [], + id: 7, + }, + }, + ], + }, + timestamp: now + 500, + }, + // scroll event on the "#container" div + { + type: EventType.IncrementalSnapshot, + data: { source: IncrementalSource.Scroll, id: 6, x: 0, y: 2500 }, + timestamp: now + 1000, + }, + // scroll event on document + { + type: EventType.IncrementalSnapshot, + data: { source: IncrementalSource.Scroll, id: 1, x: 0, y: 250 }, + timestamp: now + 1500, + }, + // remove the "#container" div + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [{ parentId: 5, id: 6 }], + adds: [], + }, + timestamp: now + 2000, + }, +]; + +export default events; diff --git a/packages/rrweb/test/events/shadow-dom.ts b/packages/rrweb/test/events/shadow-dom.ts new file mode 100644 index 0000000000..88956a833c --- /dev/null +++ b/packages/rrweb/test/events/shadow-dom.ts @@ -0,0 +1,172 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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, + }, + { + type: EventType.FullSnapshot, + 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: [], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + timestamp: now + 200, + }, + // add shadow dom elements + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 5, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 6, + isShadowHost: true, + }, + }, + ], + }, + timestamp: now + 500, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 6, + nextId: null, + node: { + type: 2, + tagName: 'span', + attributes: {}, + childNodes: [], + id: 7, + isShadow: true, + }, + }, + { + parentId: 7, + nextId: null, + node: { type: 3, textContent: 'shadow dom one', id: 8 }, + }, + ], + }, + timestamp: now + 500, + }, + // add nested shadow dom elements + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 6, + nextId: null, + node: { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + id: 9, + isShadow: true, + isShadowHost: true, + }, + }, + ], + }, + timestamp: now + 1000, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 9, + nextId: null, + node: { + type: 2, + tagName: 'span', + attributes: {}, + childNodes: [], + id: 10, + isShadow: true, + }, + }, + { + parentId: 10, + nextId: null, + node: { type: 3, textContent: 'shadow dom two', id: 11 }, + }, + ], + }, + timestamp: now + 1000, + }, +]; + +export default events; diff --git a/packages/rrweb/test/events/style-sheet-rule-events.ts b/packages/rrweb/test/events/style-sheet-rule-events.ts index 0536ea56e3..19a80bbd8a 100644 --- a/packages/rrweb/test/events/style-sheet-rule-events.ts +++ b/packages/rrweb/test/events/style-sheet-rule-events.ts @@ -105,22 +105,6 @@ const events: eventWithTime[] = [ type: EventType.FullSnapshot, timestamp: now + 100, }, - // mutation that adds style rule to existing stylesheet - { - data: { - id: 101, - adds: [ - { - rule: - '.css-added-at-400-overwritten-at-3000 {border: 1px solid blue;}', - index: 1, - }, - ], - source: IncrementalSource.StyleSheetRule, - }, - type: EventType.IncrementalSnapshot, - timestamp: now + 400, - }, // mutation that adds stylesheet { data: { @@ -142,7 +126,7 @@ const events: eventWithTime[] = [ type: 3, isStyle: true, textContent: - '\n.css-added-at-500 {\n padding: 1.3125rem;\n flex: none;\n width: 100%;\n}\n', + '\n.css-added-at-400 {\n padding: 1.3125rem;\n flex: none;\n width: 100%;\n}\n', }, nextId: null, parentId: 255, @@ -154,6 +138,22 @@ const events: eventWithTime[] = [ attributes: [], }, type: EventType.IncrementalSnapshot, + timestamp: now + 400, + }, + // mutation that adds style rule to existing stylesheet + { + data: { + id: 101, + adds: [ + { + rule: + '.css-added-at-500-overwritten-at-3000 {border: 1px solid blue;}', + index: 1, + }, + ], + source: IncrementalSource.StyleSheetRule, + }, + type: EventType.IncrementalSnapshot, timestamp: now + 500, }, // adds StyleSheetRule diff --git a/packages/rrweb/test/events/style-sheet-text-mutation.ts b/packages/rrweb/test/events/style-sheet-text-mutation.ts new file mode 100644 index 0000000000..c795f6bef2 --- /dev/null +++ b/packages/rrweb/test/events/style-sheet-text-mutation.ts @@ -0,0 +1,178 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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: 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: 101, + type: 2, + tagName: 'style', + attributes: {}, + childNodes: [ + { + id: 102, + type: 3, + isStyle: true, + textContent: '\n.css-added-at-100 {color: yellow;}\n', + }, + ], + }, + ], + }, + { + id: 107, + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // mutation that adds an element + { + data: { + adds: [ + { + node: { + id: 108, + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + }, + nextId: null, + parentId: 107, + }, + ], + texts: [], + source: IncrementalSource.Mutation, + removes: [], + attributes: [], + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 500, + }, + // adds a StyleSheetRule by inserting + { + data: { + id: 101, + adds: [ + { + rule: '.css-added-at-1000-overwritten-at-1500 {color:red;}', + }, + ], + source: IncrementalSource.StyleSheetRule, + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 1000, + }, + // adds a StyleSheetRule by adding a text + { + data: { + adds: [ + { + node: { + type: 3, + textContent: '.css-added-at-1500-deleted-at-2500 {color: yellow;}', + id: 109, + }, + nextId: null, + parentId: 101, + }, + ], + texts: [], + source: IncrementalSource.Mutation, + removes: [], + attributes: [], + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 1500, + }, + // adds a StyleSheetRule by inserting + { + data: { + id: 101, + adds: [ + { + rule: '.css-added-at-2000-overwritten-at-2500 {color: blue;}', + }, + ], + source: IncrementalSource.StyleSheetRule, + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 2000, + }, + // deletes a StyleSheetRule by removing the text + { + data: { + texts: [], + attributes: [], + removes: [{ parentId: 101, id: 109 }], + adds: [], + source: IncrementalSource.Mutation, + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 2500, + }, + // adds a StyleSheetRule by inserting + { + data: { + id: 101, + adds: [ + { + rule: '.css-added-at-3000 {color: red;}', + }, + ], + source: IncrementalSource.StyleSheetRule, + }, + type: EventType.IncrementalSnapshot, + timestamp: now + 3000, + }, +]; + +export default events; diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 3fa0f0a34d..4c92be6218 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as http from 'http'; -import * as puppeteer from 'puppeteer'; +import type * as http from 'http'; +import type * as puppeteer from 'puppeteer'; import { assertSnapshot, startServer, diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index d213760485..1d36fa5ac1 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import type * as puppeteer from 'puppeteer'; import { recordOptions, listenerHandler, diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index 9280e7fb60..143dda2373 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import type * as puppeteer from 'puppeteer'; import { recordOptions, listenerHandler, @@ -17,7 +17,7 @@ import { stripBase64, waitForRAF, } from '../utils'; -import { ICanvas } from 'rrweb-snapshot'; +import type { ICanvas } from 'rrweb-snapshot'; interface ISuite { code: string; diff --git a/packages/rrweb/test/replay/preload-all-images.test.ts b/packages/rrweb/test/replay/preload-all-images.test.ts index de7ab4e2ee..6fd9701d07 100644 --- a/packages/rrweb/test/replay/preload-all-images.test.ts +++ b/packages/rrweb/test/replay/preload-all-images.test.ts @@ -5,7 +5,6 @@ import { polyfillWebGLGlobals } from '../utils'; polyfillWebGLGlobals(); import { Replayer } from '../../src/replay'; -import {} from '../../src/types'; import { CanvasContext, CanvasArg, diff --git a/packages/rrweb/test/replay/virtual-styles.test.ts b/packages/rrweb/test/replay/virtual-styles.test.ts deleted file mode 100644 index 44d27bd925..0000000000 --- a/packages/rrweb/test/replay/virtual-styles.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { JSDOM } from 'jsdom'; -import { - applyVirtualStyleRulesToNode, - StyleRuleType, - VirtualStyleRules, -} from '../../src/replay/virtual-styles'; - -describe('virtual styles', () => { - describe('applyVirtualStyleRulesToNode', () => { - it('should insert rule at index 0 in empty sheet', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const cssText = '.added-rule {border: 1px solid yellow;}'; - - const virtualStyleRules: VirtualStyleRules = [ - { cssText, index: 0, type: StyleRuleType.Insert }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect(styleEl.sheet?.cssRules?.length).toEqual(1); - expect(styleEl.sheet?.cssRules[0].cssText).toEqual(cssText); - }); - - it('should insert rule at index 0 and keep exsisting rules', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const cssText = '.added-rule {border: 1px solid yellow;}'; - const virtualStyleRules: VirtualStyleRules = [ - { cssText, index: 0, type: StyleRuleType.Insert }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect(styleEl.sheet?.cssRules?.length).toEqual(3); - expect(styleEl.sheet?.cssRules[0].cssText).toEqual(cssText); - }); - - it('should delete rule at index 0', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const virtualStyleRules: VirtualStyleRules = [ - { index: 0, type: StyleRuleType.Remove }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect(styleEl.sheet?.cssRules?.length).toEqual(1); - expect(styleEl.sheet?.cssRules[0].cssText).toEqual('div {color: black;}'); - }); - - it('should restore a snapshot by inserting missing rules', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const virtualStyleRules: VirtualStyleRules = [ - { - cssTexts: ['a {color: blue;}', 'div {color: black;}'], - type: StyleRuleType.Snapshot, - }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect(styleEl.sheet?.cssRules?.length).toEqual(2); - }); - - it('should restore a snapshot by fixing order of rules', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const cssTexts = ['a {color: blue;}', 'div {color: black;}']; - - const virtualStyleRules: VirtualStyleRules = [ - { - cssTexts, - type: StyleRuleType.Snapshot, - }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect(styleEl.sheet?.cssRules?.length).toEqual(2); - expect( - Array.from(styleEl.sheet?.cssRules || []).map((rule) => rule.cssText), - ).toEqual(cssTexts); - }); - - // JSDOM/CSSOM is currently broken for this test - // remove '.skip' once https://github.com/NV/CSSOM/pull/113#issue-712485075 is merged - it.skip('should insert rule at index [0,0] and keep exsisting rules', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const cssText = '.added-rule {border: 1px solid yellow;}'; - const virtualStyleRules: VirtualStyleRules = [ - { cssText, index: [0, 0], type: StyleRuleType.Insert }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - console.log( - Array.from((styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules), - ); - - expect( - (styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules?.length, - ).toEqual(3); - expect( - (styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules[0].cssText, - ).toEqual(cssText); - }); - - it('should delete rule at index [0,1]', () => { - const dom = new JSDOM(` - - `); - const styleEl = dom.window.document.getElementsByTagName('style')[0]; - - const virtualStyleRules: VirtualStyleRules = [ - { index: [0, 1], type: StyleRuleType.Remove }, - ]; - applyVirtualStyleRulesToNode(virtualStyleRules, styleEl); - - expect( - (styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules?.length, - ).toEqual(1); - expect( - (styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules[0].cssText, - ).toEqual('a {color: blue;}'); - }); - }); -}); diff --git a/packages/rrweb/test/replay/webgl.test.ts b/packages/rrweb/test/replay/webgl.test.ts index f7d0498f17..754f092623 100644 --- a/packages/rrweb/test/replay/webgl.test.ts +++ b/packages/rrweb/test/replay/webgl.test.ts @@ -1,8 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; -import { assertDomSnapshot, launchPuppeteer } from '../utils'; +import { launchPuppeteer } from '../utils'; import { toMatchImageSnapshot } from 'jest-image-snapshot'; -import * as puppeteer from 'puppeteer'; +import type * as puppeteer from 'puppeteer'; import events from '../events/webgl'; interface ISuite { diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index b563732f81..bf1a5fc2d3 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -2,16 +2,21 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as puppeteer from 'puppeteer'; +import type * as puppeteer from 'puppeteer'; import { assertDomSnapshot, launchPuppeteer, sampleEvents as events, sampleStyleSheetRemoveEvents as stylesheetRemoveEvents, + waitForRAF, } from './utils'; import styleSheetRuleEvents from './events/style-sheet-rule-events'; import orderingEvents from './events/ordering'; +import scrollEvents from './events/scroll'; +import inputEvents from './events/input'; import iframeEvents from './events/iframe'; +import shadowDomEvents from './events/shadow-dom'; +import StyleSheetTextMutation from './events/style-sheet-text-mutation'; interface ISuite { code: string; @@ -247,12 +252,206 @@ describe('replayer', function () { const rules = [...replayer.iframe.contentDocument.styleSheets].map( (sheet) => [...sheet.rules], ).flat(); - rules.some((x) => x.selectorText === '.css-added-at-3100'); + rules.some((x) => x.selectorText === '.css-added-at-3100') && + !rules.some( + (x) => x.selectorText === '.css-added-at-500-overwritten-at-3000', + ); `); expect(result).toEqual(true); }); + it('should overwrite all StyleSheetRules by appending a text node to stylesheet element while fast-forwarding', async () => { + await page.evaluate(`events = ${JSON.stringify(StyleSheetTextMutation)}`); + const result = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(1600); + const rules = [...replayer.iframe.contentDocument.styleSheets].map( + (sheet) => [...sheet.rules], + ).flat(); + rules.some((x) => x.selectorText === '.css-added-at-1000-overwritten-at-1500'); + `); + expect(result).toEqual(false); + }); + + it('should apply fast-forwarded StyleSheetRules that came after appending text node to stylesheet element', async () => { + await page.evaluate(`events = ${JSON.stringify(StyleSheetTextMutation)}`); + const result = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(2100); + const rules = [...replayer.iframe.contentDocument.styleSheets].map( + (sheet) => [...sheet.rules], + ).flat(); + rules.some((x) => x.selectorText === '.css-added-at-2000-overwritten-at-2500'); + `); + expect(result).toEqual(true); + }); + + it('should overwrite all StyleSheetRules by removing text node from stylesheet element while fast-forwarding', async () => { + await page.evaluate(`events = ${JSON.stringify(StyleSheetTextMutation)}`); + const result = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(2600); + const rules = [...replayer.iframe.contentDocument.styleSheets].map( + (sheet) => [...sheet.rules], + ).flat(); + rules.some((x) => x.selectorText === '.css-added-at-2000-overwritten-at-2500'); + `); + expect(result).toEqual(false); + }); + + it('should apply fast-forwarded StyleSheetRules that came after removing text node from stylesheet element', async () => { + await page.evaluate(`events = ${JSON.stringify(StyleSheetTextMutation)}`); + const result = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(3100); + const rules = [...replayer.iframe.contentDocument.styleSheets].map( + (sheet) => [...sheet.rules], + ).flat(); + rules.some((x) => x.selectorText === '.css-added-at-3000'); + `); + expect(result).toEqual(true); + }); + + it('can fast forward scroll events', async () => { + await page.evaluate(` + events = ${JSON.stringify(scrollEvents)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.pause(550); + `); + // add the "#container" element at 500 + const iframe = await page.$('iframe'); + const contentDocument = await iframe!.contentFrame()!; + expect(await contentDocument!.$('#container')).not.toBeNull(); + expect(await contentDocument!.$('#block')).not.toBeNull(); + expect( + await contentDocument!.$eval( + '#container', + (element: Element) => element.scrollTop, + ), + ).toEqual(0); + + // restart the replayer + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + + await page.evaluate('replayer.pause(1050);'); + // scroll the "#container" div' at 1000 + expect( + await contentDocument!.$eval( + '#container', + (element: Element) => element.scrollTop, + ), + ).toEqual(2500); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(1550);'); + // scroll the document at 1500 + expect( + await page.$eval( + 'iframe', + (element: Element) => + (element as HTMLIFrameElement)!.contentWindow!.scrollY, + ), + ).toEqual(250); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(2050);'); + // remove the "#container" element at 2000 + expect(await contentDocument!.$('#container')).toBeNull(); + expect(await contentDocument!.$('#block')).toBeNull(); + expect( + await page.$eval( + 'iframe', + (element: Element) => + (element as HTMLIFrameElement)!.contentWindow!.scrollY, + ), + ).toEqual(0); + }); + + it('can fast forward input events', async () => { + await page.evaluate(` + events = ${JSON.stringify(inputEvents)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.pause(1050); + `); + const iframe = await page.$('iframe'); + const contentDocument = await iframe!.contentFrame()!; + expect(await contentDocument!.$('select')).not.toBeNull(); + expect( + await contentDocument!.$eval( + 'select', + (element: Element) => (element as HTMLSelectElement).value, + ), + ).toEqual('valueB'); // the default value + + // restart the replayer + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + + await page.evaluate('replayer.pause(1550);'); + // the value get changed to 'valueA' at 1500 + expect( + await contentDocument!.$eval( + 'select', + (element: Element) => (element as HTMLSelectElement).value, + ), + ).toEqual('valueA'); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(2050);'); + // the value get changed to 'valueC' at 2000 + expect( + await contentDocument!.$eval( + 'select', + (element: Element) => (element as HTMLSelectElement).value, + ), + ).toEqual('valueC'); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(2550);'); + // add a new input element at 2500 + expect( + await contentDocument!.$eval( + 'input', + (element: Element) => (element as HTMLSelectElement).value, + ), + ).toEqual(''); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(3050);'); + // set the value 'test input' for the input element at 3000 + expect( + await contentDocument!.$eval( + 'input', + (element: Element) => (element as HTMLSelectElement).value, + ), + ).toEqual('test input'); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(3550);'); + // remove the select element at 3500 + expect(await contentDocument!.$('select')).toBeNull(); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(4050);'); + // remove the input element at 4000 + expect(await contentDocument!.$('input')).toBeNull(); + }); + it('can fast-forward mutation events containing nested iframe elements', async () => { await page.evaluate(` events = ${JSON.stringify(iframeEvents)}; @@ -264,13 +463,12 @@ describe('replayer', function () { const contentDocument = await iframe!.contentFrame()!; expect(await contentDocument!.$('iframe')).toBeNull(); - const delay = 50; // restart the replayer await page.evaluate('replayer.play(0);'); - await page.waitForTimeout(delay); + await waitForRAF(page); await page.evaluate('replayer.pause(550);'); // add 'iframe one' at 500 expect(await contentDocument!.$('iframe')).not.toBeNull(); - const iframeOneDocument = await (await contentDocument!.$( + let iframeOneDocument = await (await contentDocument!.$( 'iframe', ))!.contentFrame(); expect(iframeOneDocument).not.toBeNull(); @@ -286,14 +484,21 @@ describe('replayer', function () { // add 'iframe two' and 'iframe three' at 1000 await page.evaluate('replayer.play(0);'); - await page.waitForTimeout(delay); + await waitForRAF(page); await page.evaluate('replayer.pause(1050);'); + // check the inserted style of iframe 'one' again + iframeOneDocument = await (await contentDocument!.$( + 'iframe', + ))!.contentFrame(); + expect((await iframeOneDocument!.$$('style')).length).toBe(1); + expect((await contentDocument!.$$('iframe')).length).toEqual(2); let iframeTwoDocument = await ( await contentDocument!.$$('iframe') )[1]!.contentFrame(); expect(iframeTwoDocument).not.toBeNull(); expect((await iframeTwoDocument!.$$('iframe')).length).toEqual(2); + expect((await iframeTwoDocument!.$$('style')).length).toBe(1); let iframeThreeDocument = await ( await iframeTwoDocument!.$$('iframe') )[0]!.contentFrame(); @@ -301,25 +506,27 @@ describe('replayer', function () { await iframeTwoDocument!.$$('iframe') )[1]!.contentFrame(); expect(iframeThreeDocument).not.toBeNull(); + expect((await iframeThreeDocument!.$$('style')).length).toBe(1); expect(iframeFourDocument).not.toBeNull(); // add 'iframe four' at 1500 await page.evaluate('replayer.play(0);'); - await page.waitForTimeout(delay); + await waitForRAF(page); await page.evaluate('replayer.pause(1550);'); iframeTwoDocument = await ( await contentDocument!.$$('iframe') )[1]!.contentFrame(); + expect((await iframeTwoDocument!.$$('style')).length).toBe(1); iframeFourDocument = await ( await iframeTwoDocument!.$$('iframe') )[1]!.contentFrame(); expect(await iframeFourDocument!.$('iframe')).toBeNull(); - expect(await iframeFourDocument!.$('style')).not.toBeNull(); + expect((await iframeFourDocument!.$$('style')).length).toBe(1); expect(await iframeFourDocument!.title()).toEqual('iframe 4'); // add 'iframe five' at 2000 await page.evaluate('replayer.play(0);'); - await page.waitForTimeout(delay); + await waitForRAF(page); await page.evaluate('replayer.pause(2050);'); iframeTwoDocument = await ( await contentDocument!.$$('iframe') @@ -327,6 +534,7 @@ describe('replayer', function () { iframeFourDocument = await ( await iframeTwoDocument!.$$('iframe') )[1]!.contentFrame(); + expect((await iframeFourDocument!.$$('style')).length).toBe(1); expect(await iframeFourDocument!.$('iframe')).not.toBeNull(); const iframeFiveDocument = await (await iframeFourDocument!.$( 'iframe', @@ -343,7 +551,7 @@ describe('replayer', function () { // remove the html element of 'iframe four' at 2500 await page.evaluate('replayer.play(0);'); - await page.waitForTimeout(delay); + await waitForRAF(page); await page.evaluate('replayer.pause(2550);'); iframeTwoDocument = await ( await contentDocument!.$$('iframe') @@ -362,6 +570,51 @@ describe('replayer', function () { ).not.toBeNull(); }); + it('can fast-forward mutation events containing nested shadow doms', async () => { + await page.evaluate(` + events = ${JSON.stringify(shadowDomEvents)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.pause(550); + `); + // add shadow dom 'one' at 500 + const iframe = await page.$('iframe'); + const contentDocument = await iframe!.contentFrame()!; + expect( + await contentDocument!.$eval('div', (element) => element.shadowRoot), + ).not.toBeNull(); + expect( + await contentDocument!.evaluate( + () => + document + .querySelector('body > div')! + .shadowRoot!.querySelector('span')!.textContent, + ), + ).toEqual('shadow dom one'); + + // add shadow dom 'two' at 1000 + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(1050);'); + expect( + await contentDocument!.evaluate( + () => + document + .querySelector('body > div')! + .shadowRoot!.querySelector('div')!.shadowRoot, + ), + ).not.toBeNull(); + expect( + await contentDocument!.evaluate( + () => + document + .querySelector('body > div')! + .shadowRoot!.querySelector('div')! + .shadowRoot!.querySelector('span')!.textContent, + ), + ).toEqual('shadow dom two'); + }); + it('can stream events in live mode', async () => { const status = await page.evaluate(` const { Replayer } = rrweb; diff --git a/packages/rrweb/tsconfig.json b/packages/rrweb/tsconfig.json index 6ac48c750c..fe6c5a6848 100644 --- a/packages/rrweb/tsconfig.json +++ b/packages/rrweb/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "module": "ESNext", "moduleResolution": "Node", - "target": "ES5", + "target": "ES6", "noImplicitAny": true, "strictNullChecks": true, "removeComments": true, @@ -10,7 +10,8 @@ "rootDir": "src", "outDir": "build", "lib": ["es6", "dom"], - "downlevelIteration": true + "downlevelIteration": true, + "importsNotUsedAsValues": "error" }, "exclude": ["test"], "include": [ diff --git a/packages/rrweb/typings/packer/base.d.ts b/packages/rrweb/typings/packer/base.d.ts index 08a8485da5..77d6837045 100644 --- a/packages/rrweb/typings/packer/base.d.ts +++ b/packages/rrweb/typings/packer/base.d.ts @@ -1,4 +1,4 @@ -import { eventWithTime } from '../types'; +import type { eventWithTime } from '../types'; export declare type PackFn = (event: eventWithTime) => string; export declare type UnpackFn = (raw: string) => eventWithTime; export declare type eventWithTimeAndPacker = eventWithTime & { diff --git a/packages/rrweb/typings/plugins/console/record/index.d.ts b/packages/rrweb/typings/plugins/console/record/index.d.ts index 0c3bc2fd61..dc8744ab08 100644 --- a/packages/rrweb/typings/plugins/console/record/index.d.ts +++ b/packages/rrweb/typings/plugins/console/record/index.d.ts @@ -1,4 +1,4 @@ -import { RecordPlugin } from '../../../types'; +import type { RecordPlugin } from '../../../types'; export declare type StringifyOptions = { stringLengthLimit?: number; numOfKeysLimit: number; diff --git a/packages/rrweb/typings/plugins/console/record/stringify.d.ts b/packages/rrweb/typings/plugins/console/record/stringify.d.ts index 213bbf3511..c1f8c9945b 100644 --- a/packages/rrweb/typings/plugins/console/record/stringify.d.ts +++ b/packages/rrweb/typings/plugins/console/record/stringify.d.ts @@ -1,2 +1,2 @@ -import { StringifyOptions } from './index'; +import type { StringifyOptions } from './index'; export declare function stringify(obj: any, stringifyOptions?: StringifyOptions): string; diff --git a/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts b/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts index 3311e19b3c..a2f86c3cee 100644 --- a/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts +++ b/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts @@ -1,4 +1,4 @@ -import { RecordPlugin } from '../../../types'; +import type { RecordPlugin } from '../../../types'; export declare type SequentialIdOptions = { key: string; }; diff --git a/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts b/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts index a1eee69e1b..a8f7e80c0e 100644 --- a/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts +++ b/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts @@ -1,5 +1,5 @@ import type { SequentialIdOptions } from '../record'; -import { ReplayPlugin } from '../../../types'; +import type { ReplayPlugin } from '../../../types'; declare type Options = SequentialIdOptions & { warnOnMissingId: boolean; }; diff --git a/packages/rrweb/typings/record/iframe-manager.d.ts b/packages/rrweb/typings/record/iframe-manager.d.ts index 9fbf136cc4..4dc8c120b3 100644 --- a/packages/rrweb/typings/record/iframe-manager.d.ts +++ b/packages/rrweb/typings/record/iframe-manager.d.ts @@ -1,5 +1,5 @@ -import { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import { mutationCallBack } from '../types'; +import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; +import type { mutationCallBack } from '../types'; export declare class IframeManager { private iframes; private mutationCb; diff --git a/packages/rrweb/typings/record/mutation.d.ts b/packages/rrweb/typings/record/mutation.d.ts index 5f88a664f9..930d247848 100644 --- a/packages/rrweb/typings/record/mutation.d.ts +++ b/packages/rrweb/typings/record/mutation.d.ts @@ -1,4 +1,4 @@ -import { mutationRecord, MutationBufferParam } from '../types'; +import type { mutationRecord, MutationBufferParam } from '../types'; export default class MutationBuffer { private frozen; private locked; diff --git a/packages/rrweb/typings/record/observers/canvas/2d.d.ts b/packages/rrweb/typings/record/observers/canvas/2d.d.ts index fee00970e9..febe6b226d 100644 --- a/packages/rrweb/typings/record/observers/canvas/2d.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/2d.d.ts @@ -1,3 +1,3 @@ -import { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; export default function initCanvas2DMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts index 46635a453a..d0ba11718f 100644 --- a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts @@ -1,5 +1,5 @@ -import { Mirror } from 'rrweb-snapshot'; -import { blockClass, canvasMutationCallback, IWindow } from '../../../types'; +import type { Mirror } from 'rrweb-snapshot'; +import type { blockClass, canvasMutationCallback, IWindow } from '../../../types'; export declare type RafStamps = { latestId: number; invokeId: number | null; diff --git a/packages/rrweb/typings/record/observers/canvas/canvas.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas.d.ts index 359d95928d..c35e97aa55 100644 --- a/packages/rrweb/typings/record/observers/canvas/canvas.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/canvas.d.ts @@ -1,2 +1,2 @@ -import { blockClass, IWindow, listenerHandler } from '../../../types'; +import type { blockClass, IWindow, listenerHandler } from '../../../types'; export default function initCanvasContextObserver(win: IWindow, blockClass: blockClass): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts b/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts index 10337d4e2c..b4d17f897b 100644 --- a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts @@ -1,4 +1,4 @@ -import { IWindow, CanvasArg } from '../../../types'; +import type { IWindow, CanvasArg } from '../../../types'; export declare function variableListFor(ctx: RenderingContext, ctor: string): any[]; export declare const saveWebGLVar: (value: any, win: IWindow, ctx: RenderingContext) => number | void; export declare function serializeArg(value: any, win: IWindow, ctx: RenderingContext): CanvasArg; diff --git a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts b/packages/rrweb/typings/record/observers/canvas/webgl.d.ts index 7f866cd8e0..f5bceaeb22 100644 --- a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts +++ b/packages/rrweb/typings/record/observers/canvas/webgl.d.ts @@ -1,3 +1,3 @@ -import { Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; export default function initCanvasWebGLMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/shadow-dom-manager.d.ts b/packages/rrweb/typings/record/shadow-dom-manager.d.ts index 7f973a93d8..bf6a306c8f 100644 --- a/packages/rrweb/typings/record/shadow-dom-manager.d.ts +++ b/packages/rrweb/typings/record/shadow-dom-manager.d.ts @@ -1,5 +1,5 @@ -import { mutationCallBack, scrollCallback, MutationBufferParam, SamplingStrategy } from '../types'; -import { Mirror } from 'rrweb-snapshot'; +import type { mutationCallBack, scrollCallback, MutationBufferParam, SamplingStrategy } from '../types'; +import type { Mirror } from 'rrweb-snapshot'; declare type BypassOptions = Omit & { sampling: SamplingStrategy; }; diff --git a/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts b/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts index 71ca36c5b2..ca58dc6b17 100644 --- a/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts +++ b/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts @@ -1,4 +1,4 @@ -import { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse } from '../../types'; +import type { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse } from '../../types'; export interface ImageBitmapDataURLRequestWorker { postMessage: (message: ImageBitmapDataURLWorkerParams, transfer?: [ImageBitmap]) => void; onmessage: (message: MessageEvent) => void; diff --git a/packages/rrweb/typings/replay/canvas/2d.d.ts b/packages/rrweb/typings/replay/canvas/2d.d.ts index d780a7b981..ca7485046a 100644 --- a/packages/rrweb/typings/replay/canvas/2d.d.ts +++ b/packages/rrweb/typings/replay/canvas/2d.d.ts @@ -1,5 +1,5 @@ -import { Replayer } from '../'; -import { canvasMutationCommand } from '../../types'; +import type { Replayer } from '../'; +import type { canvasMutationCommand } from '../../types'; export default function canvasMutation({ event, mutation, target, imageMap, errorHandler, }: { event: Parameters[0]; mutation: canvasMutationCommand; diff --git a/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts b/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts index 4d0c00e708..dd7d5a6268 100644 --- a/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts +++ b/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts @@ -1,5 +1,5 @@ import type { Replayer } from '../'; -import { CanvasArg, SerializedCanvasArg } from '../../types'; +import type { CanvasArg, SerializedCanvasArg } from '../../types'; export declare function variableListFor(ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext, ctor: string): any[]; export declare function isSerializedArg(arg: unknown): arg is SerializedCanvasArg; export declare function deserializeArg(imageMap: Replayer['imageMap'], ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext | null, preload?: { diff --git a/packages/rrweb/typings/replay/canvas/index.d.ts b/packages/rrweb/typings/replay/canvas/index.d.ts index 95b43accae..e316e3ea51 100644 --- a/packages/rrweb/typings/replay/canvas/index.d.ts +++ b/packages/rrweb/typings/replay/canvas/index.d.ts @@ -1,4 +1,4 @@ -import { Replayer } from '..'; +import type { Replayer } from '..'; import { canvasMutationData } from '../../types'; export default function canvasMutation({ event, mutation, target, imageMap, canvasEventMap, errorHandler, }: { event: Parameters[0]; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index f298737c52..46b84e58c4 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -1,4 +1,5 @@ import { Mirror } from 'rrweb-snapshot'; +import { RRDocument } from 'rrdom/es/virtual-dom'; import { Timer } from './timer'; import { createPlayerService, createSpeedService } from './machine'; import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types'; @@ -10,16 +11,14 @@ export declare class Replayer { speedService: ReturnType; get timer(): Timer; config: playerConfig; + usingVirtualDom: boolean; + virtualDom: RRDocument; private mouse; private mouseTail; private tailPositions; private emitter; private nextUserInteractionEvent; private legacy_missingNodeRetryMap; - private treeIndex; - private fragmentParentMap; - private elementStateMap; - private virtualStyleRulesMap; private cache; private imageMap; private canvasEventMap; @@ -62,17 +61,12 @@ export declare class Replayer { private applyMutation; private applyScroll; private applyInput; - private applyText; private legacy_resolveMissingNode; private moveAndHover; private drawMouseTail; private hoverElements; private isUserInteraction; private backToNormal; - private restoreRealParent; - private storeState; - private restoreState; - private restoreNodeSheet; private warnNodeNotFound; private warnCanvasMutationFailed; private debugNodeNotFound; diff --git a/packages/rrweb/typings/replay/virtual-styles.d.ts b/packages/rrweb/typings/replay/virtual-styles.d.ts deleted file mode 100644 index ad37eed73c..0000000000 --- a/packages/rrweb/typings/replay/virtual-styles.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -export declare enum StyleRuleType { - Insert = 0, - Remove = 1, - Snapshot = 2, - SetProperty = 3, - RemoveProperty = 4 -} -declare type InsertRule = { - cssText: string; - type: StyleRuleType.Insert; - index?: number | number[]; -}; -declare type RemoveRule = { - type: StyleRuleType.Remove; - index: number | number[]; -}; -declare type SnapshotRule = { - type: StyleRuleType.Snapshot; - cssTexts: string[]; -}; -declare type SetPropertyRule = { - type: StyleRuleType.SetProperty; - index: number[]; - property: string; - value: string | null; - priority: string | undefined; -}; -declare type RemovePropertyRule = { - type: StyleRuleType.RemoveProperty; - index: number[]; - property: string; -}; -export declare type VirtualStyleRules = Array; -export declare type VirtualStyleRulesMap = Map; -export declare function getNestedRule(rules: CSSRuleList, position: number[]): CSSGroupingRule; -export declare function getPositionsAndIndex(nestedIndex: number[]): { - positions: number[]; - index: number | undefined; -}; -export declare function applyVirtualStyleRulesToNode(storedRules: VirtualStyleRules, styleNode: HTMLStyleElement): void; -export declare function storeCSSRules(parentElement: HTMLStyleElement, virtualStyleRulesMap: VirtualStyleRulesMap): void; -export {}; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 03ce628bd6..61f3d8b84c 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -1,9 +1,10 @@ -import { serializedNodeWithId, Mirror, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot'; -import { PackFn, UnpackFn } from './packer/base'; -import { IframeManager } from './record/iframe-manager'; -import { ShadowDomManager } from './record/shadow-dom-manager'; +import type { serializedNodeWithId, Mirror, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot'; +import type { PackFn, UnpackFn } from './packer/base'; +import type { IframeManager } from './record/iframe-manager'; +import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import { CanvasManager } from './record/observers/canvas/canvas-manager'; +import type { RRNode } from 'rrdom/es/virtual-dom'; +import type { CanvasManager } from './record/observers/canvas/canvas-manager'; export declare enum EventType { DomContentLoaded = 0, Load = 1, @@ -115,6 +116,10 @@ export declare type eventWithTime = event & { timestamp: number; delay?: number; }; +export declare type canvasEventWithTime = eventWithTime & { + type: EventType.IncrementalSnapshot; + data: canvasMutationData; +}; export declare type blockClass = string | RegExp; export declare type maskTextClass = string | RegExp; export declare type SamplingStrategy = Partial<{ @@ -464,6 +469,7 @@ export declare type playerConfig = { strokeStyle?: string; }; unpackFn?: UnpackFn; + useVirtualDom: boolean; plugins?: ReplayPlugin[]; }; export declare type playerMetaData = { @@ -472,7 +478,7 @@ export declare type playerMetaData = { totalTime: number; }; export declare type missingNode = { - node: Node; + node: Node | RRNode; mutation: addedNodeMutation; }; export declare type missingNodeMap = { @@ -507,9 +513,6 @@ export declare enum ReplayerEvents { StateChange = "state-change", PlayBack = "play-back" } -export declare type ElementState = { - scroll?: [number, number]; -}; export declare type KeepIframeSrcFn = (src: string) => boolean; declare global { interface Window { diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index fb6dcaba94..0dacfb8245 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -1,5 +1,6 @@ -import { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, removedNodeMutation, textMutation, attributeMutation, mutationData, scrollData, inputData, DocumentDimension, IWindow, DeprecatedMirror } from './types'; -import { Mirror } from 'rrweb-snapshot'; +import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types'; +import type { IMirror, Mirror } from 'rrweb-snapshot'; +import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom'; 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; @@ -15,38 +16,6 @@ export declare function isIgnored(n: Node, mirror: Mirror): boolean; export declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean; export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent; export declare function polyfill(win?: Window & typeof globalThis): void; -export declare type TreeNode = { - id: number; - mutation: addedNodeMutation; - parent?: TreeNode; - children: Record; - texts: textMutation[]; - attributes: attributeMutation[]; -}; -export declare class TreeIndex { - tree: Record; - private removeNodeMutations; - private textMutations; - private attributeMutations; - private indexes; - private removeIdSet; - private scrollMap; - private inputMap; - constructor(); - add(mutation: addedNodeMutation): void; - remove(mutation: removedNodeMutation, mirror: Mirror): void; - text(mutation: textMutation): void; - attribute(mutation: attributeMutation): void; - scroll(d: scrollData): void; - input(d: inputData): void; - flush(): { - mutationData: mutationData; - scrollMap: TreeIndex['scrollMap']; - inputMap: TreeIndex['inputMap']; - }; - private reset; - idRemoved(id: number): boolean; -} declare type ResolveTree = { value: addedNodeMutation; children: ResolveTree[]; @@ -56,12 +25,17 @@ export declare function queueToResolveTrees(queue: addedNodeMutation[]): Resolve export declare function iterateResolveTree(tree: ResolveTree, cb: (mutation: addedNodeMutation) => unknown): void; export declare type AppendedIframe = { mutationInQueue: addedNodeMutation; - builtNode: HTMLIFrameElement; + builtNode: HTMLIFrameElement | RRIFrameElement; }; -export declare function isSerializedIframe(n: Node, mirror: Mirror): n is HTMLIFrameElement; +export declare function isSerializedIframe(n: TNode, mirror: IMirror): boolean; export declare function getBaseDimension(node: Node, rootIframe: Node): DocumentDimension; -export declare function hasShadowRoot(n: T): n is T & { +export declare function hasShadowRoot(n: T): n is T & { shadowRoot: ShadowRoot; }; -export declare function getUniqueTextMutations(mutations: textMutation[]): textMutation[]; +export declare function getNestedRule(rules: CSSRuleList, position: number[]): CSSGroupingRule; +export declare function getPositionsAndIndex(nestedIndex: number[]): { + positions: number[]; + index: number | undefined; +}; +export declare function uniqueTextMutations(mutations: textMutation[]): textMutation[]; export {}; diff --git a/yarn.lock b/yarn.lock index 19feb977df..a438248446 100644 --- a/yarn.lock +++ b/yarn.lock @@ -571,18 +571,6 @@ jest-util "^27.2.4" slash "^3.0.0" -"@jest/console@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.6.tgz#0742e6787f682b22bdad56f9db2a8a77f6a86107" - integrity sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA== - dependencies: - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^27.4.6" - jest-util "^27.4.2" - slash "^3.0.0" - "@jest/console@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" @@ -629,40 +617,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^27.4.7": - version "27.4.7" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913" - integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg== - dependencies: - "@jest/console" "^27.4.6" - "@jest/reporters" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^27.4.2" - jest-config "^27.4.7" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-resolve-dependencies "^27.4.6" - jest-runner "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" - jest-watcher "^27.4.6" - micromatch "^4.0.4" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/core@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" @@ -707,16 +661,6 @@ "@types/node" "*" jest-mock "^27.2.4" -"@jest/environment@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.6.tgz#1e92885d64f48c8454df35ed9779fbcf31c56d8b" - integrity sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg== - dependencies: - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - jest-mock "^27.4.6" - "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -739,18 +683,6 @@ jest-mock "^27.2.4" jest-util "^27.2.4" -"@jest/fake-timers@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.6.tgz#e026ae1671316dbd04a56945be2fa251204324e8" - integrity sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A== - dependencies: - "@jest/types" "^27.4.2" - "@sinonjs/fake-timers" "^8.0.1" - "@types/node" "*" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-util "^27.4.2" - "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -772,15 +704,6 @@ "@jest/types" "^27.2.4" expect "^27.2.4" -"@jest/globals@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.6.tgz#3f09bed64b0fd7f5f996920258bd4be8f52f060a" - integrity sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/types" "^27.4.2" - expect "^27.4.6" - "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -820,37 +743,6 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/reporters@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.6.tgz#b53dec3a93baf9b00826abf95b932de919d6d8dd" - integrity sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.4" - 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-haste-map "^27.4.6" - jest-resolve "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" - "@jest/reporters@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" @@ -891,15 +783,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^27.4.0": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" - integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.4" - source-map "^0.6.0" - "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -919,16 +802,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.6.tgz#b3df94c3d899c040f602cea296979844f61bdf69" - integrity sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ== - dependencies: - "@jest/console" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" @@ -949,16 +822,6 @@ jest-haste-map "^27.2.4" jest-runtime "^27.2.4" -"@jest/test-sequencer@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz#447339b8a3d7b5436f50934df30854e442a9d904" - integrity sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw== - dependencies: - "@jest/test-result" "^27.4.6" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-runtime "^27.4.6" - "@jest/test-sequencer@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" @@ -990,27 +853,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^27.4.6": - version "27.4.6" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.6.tgz#153621940b1ed500305eacdb31105d415dc30231" - integrity sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.4.2" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-regex-util "^27.4.0" - jest-util "^27.4.2" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - "@jest/transform@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" @@ -1043,17 +885,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" - integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" @@ -1939,10 +1770,10 @@ magic-string "^0.25.7" resolve "^1.17.0" -"@rollup/plugin-commonjs@^21.0.2": - version "21.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.2.tgz#0b9c539aa1837c94abfaf87945838b0fc8564891" - integrity sha512-d/OmjaLVO4j/aQX69bwpWPpbvI3TJkQuxoAk7BH8ew1PyoMBLTOuvJTjzG8oEoW7drIIqB0KCJtfFLu/2GClWg== +"@rollup/plugin-commonjs@^22.0.0": + version "22.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.0.tgz#f4d87016e2fbf187a593ab9f46626fe05b59e8bd" + integrity sha512-Ktvf2j+bAO+30awhbYoCaXpBcyPmJbaEUYClQns/+6SNCYFURbvBiNbWgHITEsIgDDWCDUclWRKEuf8cwZCFoQ== dependencies: "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" @@ -1976,24 +1807,17 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-node-resolve@^7.0.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" - integrity sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q== +"@rollup/plugin-node-resolve@^13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz#cdee815cf02c180ff0a42536ca67a8f67e299f84" + integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== dependencies: - "@rollup/pluginutils" "^3.0.8" - "@types/resolve" "0.0.8" + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" builtin-modules "^3.1.0" + deepmerge "^4.2.2" is-module "^1.0.0" - resolve "^1.14.2" - -"@rollup/plugin-typescript@^4.0.0": - version "4.1.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-4.1.2.tgz#6f910430276ae3e53a47a12ad65820627e7b6ad9" - integrity sha512-+7UlGat/99e2JbmGNnIauxwEhYLwrL7adO/tSJxUN57xrrS3Ps+ZzYpLCDGPZJ57j+ZJTZLLN89KXW9JMEB+jg== - dependencies: - "@rollup/pluginutils" "^3.0.1" - resolve "^1.14.1" + resolve "^1.19.0" "@rollup/plugin-typescript@^8.2.5": version "8.2.5" @@ -2003,7 +1827,15 @@ "@rollup/pluginutils" "^3.1.0" resolve "^1.17.0" -"@rollup/pluginutils@4", "@rollup/pluginutils@^4.1.0": +"@rollup/plugin-typescript@^8.3.2": + version "8.3.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz#e1b719e2ed3e752bbc092001656c48378f2d15f0" + integrity sha512-MtgyR5LNHZr3GyN0tM7gNO9D0CS+Y+vflS4v/PHmrX17JCkHUYKvQ5jN5o3cz1YKllM3duXUqu3yOHwMPUxhDg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + resolve "^1.17.0" + +"@rollup/pluginutils@4": version "4.1.1" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ== @@ -2011,7 +1843,7 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@rollup/pluginutils@^3.0.1", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": +"@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -2020,6 +1852,14 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^4.1.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + "@rollup/pluginutils@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751" @@ -2120,6 +1960,11 @@ resolved "https://registry.yarnpkg.com/@types/cssom/-/cssom-0.4.1.tgz#fb64e145b425bd6c1b0ed78ebd66ba43b6e088ab" integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ== +"@types/cssstyle@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/cssstyle/-/cssstyle-2.2.1.tgz#fa010824006ff47af94a6b9baf9759e031815347" + integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -2186,14 +2031,6 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" -"@types/jest@^27.0.1": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.0.tgz#037ab8b872067cae842a320841693080f9cb84ed" - integrity sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ== - dependencies: - jest-diff "^27.0.0" - pretty-format "^27.0.0" - "@types/jest@^27.4.1": version "27.4.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" @@ -2202,15 +2039,6 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/jsdom@^16.2.14": - version "16.2.14" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.14.tgz#26fe9da6a8870715b154bb84cd3b2e53433d8720" - integrity sha512-6BAy1xXEmMuHeAJ4Fv4yXKwBDTGTOseExKE3OaHiNycdHdZw59KfYzrt0DkDluvwmik1HRt6QS7bImxUmpSy+w== - dependencies: - "@types/node" "*" - "@types/parse5" "*" - "@types/tough-cookie" "*" - "@types/jsdom@^16.2.4": version "16.2.13" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f" @@ -2316,13 +2144,6 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -2602,11 +2423,16 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.4.1: +acorn@^8.2.4: version "8.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.4.1: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -2908,20 +2734,6 @@ babel-jest@^27.2.4: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.6.tgz#4d024e69e241cdf4f396e453a07100f44f7ce314" - integrity sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg== - dependencies: - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.4.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - slash "^3.0.0" - babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -2968,16 +2780,6 @@ babel-plugin-jest-hoist@^27.2.0: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" - integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" @@ -3014,14 +2816,6 @@ babel-preset-jest@^27.2.0: babel-plugin-jest-hoist "^27.2.0" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" - integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== - dependencies: - babel-plugin-jest-hoist "^27.4.0" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" @@ -3669,6 +3463,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +compare-versions@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" + integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4092,12 +3891,7 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@^0.5.0: +cssom@^0.4.4, 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== @@ -4135,15 +3929,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -data-urls@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.0.tgz#3ff551c986d7c6234a0ac4bbf20a269e1cd6b378" - integrity sha512-4AefxbTTdFtxDUdh0BuMBs2qJVL25Mow2zlcuuePegQwgD6GEmQao42LLEeksOui8nL4RcNEugIpFP7eRd33xg== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^9.0.0" - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -4170,6 +3955,13 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4188,7 +3980,7 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decimal.js@^10.2.1, decimal.js@^10.3.1: +decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== @@ -4290,11 +4082,6 @@ diff-sequences@^27.0.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== -diff-sequences@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" - integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== - diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" @@ -4557,6 +4344,11 @@ es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-module-lexer@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4578,6 +4370,132 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +esbuild-android-64@0.14.38: + version "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-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-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-arm64@0.14.38: + version "0.14.38" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz#01eb6650ec010b18c990e443a6abcca1d71290a9" + integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== + +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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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@^0.14.38: + version "0.14.38" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.38.tgz#99526b778cd9f35532955e26e1709a16cca2fb30" + integrity sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA== + optionalDependencies: + esbuild-android-64 "0.14.38" + esbuild-android-arm64 "0.14.38" + esbuild-darwin-64 "0.14.38" + esbuild-darwin-arm64 "0.14.38" + esbuild-freebsd-64 "0.14.38" + esbuild-freebsd-arm64 "0.14.38" + esbuild-linux-32 "0.14.38" + esbuild-linux-64 "0.14.38" + esbuild-linux-arm "0.14.38" + esbuild-linux-arm64 "0.14.38" + esbuild-linux-mips64le "0.14.38" + esbuild-linux-ppc64le "0.14.38" + esbuild-linux-riscv64 "0.14.38" + esbuild-linux-s390x "0.14.38" + esbuild-netbsd-64 "0.14.38" + esbuild-openbsd-64 "0.14.38" + esbuild-sunos-64 "0.14.38" + esbuild-windows-32 "0.14.38" + esbuild-windows-64 "0.14.38" + esbuild-windows-arm64 "0.14.38" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -4823,16 +4741,6 @@ expect@^27.2.4: jest-message-util "^27.2.4" jest-regex-util "^27.0.6" -expect@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" - integrity sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag== - dependencies: - "@jest/types" "^27.4.2" - jest-get-type "^27.4.0" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - expect@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" @@ -5075,7 +4983,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^3.3.1, find-cache-dir@^3.3.2: +find-cache-dir@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== @@ -5160,15 +5068,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.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -5193,15 +5092,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" @@ -6434,15 +6324,6 @@ jest-changed-files@^27.2.4: execa "^5.0.0" throat "^6.0.1" -jest-changed-files@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" - integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== - dependencies: - "@jest/types" "^27.4.2" - execa "^5.0.0" - throat "^6.0.1" - jest-changed-files@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" @@ -6477,31 +6358,6 @@ jest-circus@^27.2.4: stack-utils "^2.0.3" throat "^6.0.1" -jest-circus@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.6.tgz#d3af34c0eb742a967b1919fbb351430727bcea6c" - integrity sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - expect "^27.4.6" - is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" - slash "^3.0.0" - stack-utils "^2.0.3" - throat "^6.0.1" - jest-circus@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" @@ -6545,24 +6401,6 @@ jest-cli@^27.2.4: prompts "^2.0.1" yargs "^16.2.0" -jest-cli@^27.4.7: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a" - integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw== - dependencies: - "@jest/core" "^27.4.7" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - import-local "^3.0.2" - jest-config "^27.4.7" - jest-util "^27.4.2" - jest-validate "^27.4.6" - prompts "^2.0.1" - yargs "^16.2.0" - jest-cli@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" @@ -6608,34 +6446,6 @@ jest-config@^27.2.4: micromatch "^4.0.4" pretty-format "^27.2.4" -jest-config@^27.4.7: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972" - integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw== - dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.4.6" - "@jest/types" "^27.4.2" - babel-jest "^27.4.6" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.4" - jest-circus "^27.4.6" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-get-type "^27.4.0" - jest-jasmine2 "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-runner "^27.4.6" - jest-util "^27.4.2" - jest-validate "^27.4.6" - micromatch "^4.0.4" - pretty-format "^27.4.6" - slash "^3.0.0" - jest-config@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" @@ -6686,16 +6496,6 @@ jest-diff@^27.0.0, jest-diff@^27.2.4: jest-get-type "^27.0.6" pretty-format "^27.2.4" -jest-diff@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" - integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.4.0" - jest-get-type "^27.4.0" - pretty-format "^27.4.6" - jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -6713,13 +6513,6 @@ jest-docblock@^27.0.6: dependencies: detect-newline "^3.0.0" -jest-docblock@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" - integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -6738,17 +6531,6 @@ jest-each@^27.2.4: jest-util "^27.2.4" pretty-format "^27.2.4" -jest-each@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.6.tgz#e7e8561be61d8cc6dbf04296688747ab186c40ff" - integrity sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA== - dependencies: - "@jest/types" "^27.4.2" - chalk "^4.0.0" - jest-get-type "^27.4.0" - jest-util "^27.4.2" - pretty-format "^27.4.6" - jest-each@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" @@ -6773,19 +6555,6 @@ jest-environment-jsdom@^27.2.4: jest-util "^27.2.4" jsdom "^16.6.0" -jest-environment-jsdom@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz#c23a394eb445b33621dfae9c09e4c8021dea7b36" - integrity sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" - jsdom "^16.6.0" - jest-environment-jsdom@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" @@ -6811,18 +6580,6 @@ jest-environment-node@^27.2.4: jest-mock "^27.2.4" jest-util "^27.2.4" -jest-environment-node@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.6.tgz#ee8cd4ef458a0ef09d087c8cd52ca5856df90242" - integrity sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - jest-mock "^27.4.6" - jest-util "^27.4.2" - jest-environment-node@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" @@ -6845,11 +6602,6 @@ jest-get-type@^27.0.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== -jest-get-type@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" - integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== - jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" @@ -6875,26 +6627,6 @@ jest-haste-map@^27.2.4: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.6.tgz#c60b5233a34ca0520f325b7e2cc0a0140ad0862a" - integrity sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ== - dependencies: - "@jest/types" "^27.4.2" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^27.4.0" - jest-serializer "^27.4.0" - jest-util "^27.4.2" - jest-worker "^27.4.6" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" @@ -6954,29 +6686,6 @@ jest-jasmine2@^27.2.4: pretty-format "^27.2.4" throat "^6.0.1" -jest-jasmine2@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz#109e8bc036cb455950ae28a018f983f2abe50127" - integrity sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.4.6" - is-generator-fn "^2.0.0" - jest-each "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-runtime "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - pretty-format "^27.4.6" - throat "^6.0.1" - jest-jasmine2@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" @@ -7008,14 +6717,6 @@ jest-leak-detector@^27.2.4: jest-get-type "^27.0.6" pretty-format "^27.2.4" -jest-leak-detector@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz#ed9bc3ce514b4c582637088d9faf58a33bd59bf4" - integrity sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA== - dependencies: - jest-get-type "^27.4.0" - pretty-format "^27.4.6" - jest-leak-detector@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" @@ -7053,16 +6754,6 @@ jest-matcher-utils@^27.2.4: jest-get-type "^27.0.6" pretty-format "^27.2.4" -jest-matcher-utils@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz#53ca7f7b58170638590e946f5363b988775509b8" - integrity sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA== - dependencies: - chalk "^4.0.0" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - pretty-format "^27.4.6" - jest-message-util@^23.4.0: version "23.4.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" @@ -7089,21 +6780,6 @@ jest-message-util@^27.2.4: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.6.tgz#9fdde41a33820ded3127465e1a5896061524da31" - integrity sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.4.2" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - pretty-format "^27.4.6" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -7127,14 +6803,6 @@ jest-mock@^27.2.4: "@jest/types" "^27.2.4" "@types/node" "*" -jest-mock@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.6.tgz#77d1ba87fbd33ccb8ef1f061697e7341b7635195" - integrity sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw== - dependencies: - "@jest/types" "^27.4.2" - "@types/node" "*" - jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -7153,11 +6821,6 @@ jest-regex-util@^27.0.6: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== -jest-regex-util@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" - integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== - jest-regex-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" @@ -7172,15 +6835,6 @@ jest-resolve-dependencies@^27.2.4: jest-regex-util "^27.0.6" jest-snapshot "^27.2.4" -jest-resolve-dependencies@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz#fc50ee56a67d2c2183063f6a500cc4042b5e2327" - integrity sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw== - dependencies: - "@jest/types" "^27.4.2" - jest-regex-util "^27.4.0" - jest-snapshot "^27.4.6" - jest-resolve-dependencies@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" @@ -7215,22 +6869,6 @@ jest-resolve@^27.2.4: resolve "^1.20.0" slash "^3.0.0" -jest-resolve@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.6.tgz#2ec3110655e86d5bfcfa992e404e22f96b0b5977" - integrity sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw== - dependencies: - "@jest/types" "^27.4.2" - chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-pnp-resolver "^1.2.2" - jest-util "^27.4.2" - jest-validate "^27.4.6" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" @@ -7275,34 +6913,6 @@ jest-runner@^27.2.4: source-map-support "^0.5.6" throat "^6.0.1" -jest-runner@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.6.tgz#1d390d276ec417e9b4d0d081783584cbc3e24773" - integrity sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg== - dependencies: - "@jest/console" "^27.4.6" - "@jest/environment" "^27.4.6" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.4.0" - jest-environment-jsdom "^27.4.6" - jest-environment-node "^27.4.6" - jest-haste-map "^27.4.6" - jest-leak-detector "^27.4.6" - jest-message-util "^27.4.6" - jest-resolve "^27.4.6" - jest-runtime "^27.4.6" - jest-util "^27.4.2" - jest-worker "^27.4.6" - source-map-support "^0.5.6" - throat "^6.0.1" - jest-runner@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" @@ -7363,34 +6973,6 @@ jest-runtime@^27.2.4: strip-bom "^4.0.0" yargs "^16.2.0" -jest-runtime@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.6.tgz#83ae923818e3ea04463b22f3597f017bb5a1cffa" - integrity sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ== - dependencies: - "@jest/environment" "^27.4.6" - "@jest/fake-timers" "^27.4.6" - "@jest/globals" "^27.4.6" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.6" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.6" - jest-message-util "^27.4.6" - jest-mock "^27.4.6" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.6" - jest-snapshot "^27.4.6" - jest-util "^27.4.2" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" @@ -7427,14 +7009,6 @@ jest-serializer@^27.0.6: "@types/node" "*" graceful-fs "^4.2.4" -jest-serializer@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" - integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - jest-serializer@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" @@ -7489,34 +7063,6 @@ jest-snapshot@^27.2.4: pretty-format "^27.2.4" semver "^7.3.2" -jest-snapshot@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.6.tgz#e2a3b4fff8bdce3033f2373b2e525d8b6871f616" - integrity sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ== - dependencies: - "@babel/core" "^7.7.2" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^27.4.6" - graceful-fs "^4.2.4" - jest-diff "^27.4.6" - jest-get-type "^27.4.0" - jest-haste-map "^27.4.6" - jest-matcher-utils "^27.4.6" - jest-message-util "^27.4.6" - jest-util "^27.4.2" - natural-compare "^1.4.0" - pretty-format "^27.4.6" - semver "^7.3.2" - jest-snapshot@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" @@ -7557,18 +7103,6 @@ jest-util@^27.0.0, jest-util@^27.2.4: is-ci "^3.0.0" picomatch "^2.2.3" -jest-util@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" - integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== - dependencies: - "@jest/types" "^27.4.2" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.4" - picomatch "^2.2.3" - jest-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" @@ -7593,18 +7127,6 @@ jest-validate@^27.2.4: leven "^3.1.0" pretty-format "^27.2.4" -jest-validate@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.6.tgz#efc000acc4697b6cf4fa68c7f3f324c92d0c4f1f" - integrity sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ== - dependencies: - "@jest/types" "^27.4.2" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^27.4.0" - leven "^3.1.0" - pretty-format "^27.4.6" - jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" @@ -7630,19 +7152,6 @@ jest-watcher@^27.2.4: jest-util "^27.2.4" string-length "^4.0.1" -jest-watcher@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.6.tgz#673679ebeffdd3f94338c24f399b85efc932272d" - integrity sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw== - dependencies: - "@jest/test-result" "^27.4.6" - "@jest/types" "^27.4.2" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^27.4.2" - string-length "^4.0.1" - jest-watcher@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" @@ -7674,15 +7183,6 @@ jest-worker@^27.2.4: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" - integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -7692,15 +7192,6 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.1.1: - version "27.4.7" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4" - integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg== - dependencies: - "@jest/core" "^27.4.7" - import-local "^3.0.2" - jest-cli "^27.4.7" - jest@^27.2.4: version "27.2.4" resolved "https://registry.yarnpkg.com/jest/-/jest-27.2.4.tgz#70e27bef873138afc123aa4769f7124c50ad3efb" @@ -7719,6 +7210,11 @@ jest@^27.5.1: import-local "^3.0.2" jest-cli "^27.5.1" +joycon@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -7742,11 +7238,6 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom-global@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9" - integrity sha1-a9KZwTsMRiay2iwDk81DhdYGrLk= - jsdom@^16.4.0: version "16.6.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" @@ -7813,39 +7304,6 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" -jsdom@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-17.0.0.tgz#3ec82d1d30030649c8defedc45fff6aa3e5d06ae" - integrity sha512-MUq4XdqwtNurZDVeKScENMPHnkgmdIvMzZ1r1NSwHkDuaqI6BouPjr+17COo4/19oLNnmdpFDPOHVpgIZmZ+VA== - dependencies: - abab "^2.0.5" - acorn "^8.4.1" - acorn-globals "^6.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.0" - decimal.js "^10.3.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^9.0.0" - ws "^8.0.0" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7900,12 +7358,10 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" +jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== jsonfile@^6.0.1: version "6.1.0" @@ -9823,15 +9279,6 @@ pretty-format@^27.0.0, pretty-format@^27.2.4: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^27.4.6: - version "27.4.6" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" - integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -10316,7 +9763,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.20.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: +resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -10390,6 +9837,17 @@ rollup-plugin-css-only@^3.1.0: dependencies: "@rollup/pluginutils" "4" +rollup-plugin-esbuild@^4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-esbuild/-/rollup-plugin-esbuild-4.9.1.tgz#369d137e2b1542c8ee459495fd4f10de812666aa" + integrity sha512-qn/x7Wz9p3Xnva99qcb+nopH0d2VJwVnsxJTGEg+Sh2Z3tqQl33MhOwzekVo1YTKgv+yAmosjcBRJygMfGrtLw== + dependencies: + "@rollup/pluginutils" "^4.1.1" + debug "^4.3.3" + es-module-lexer "^0.9.3" + joycon "^3.0.1" + jsonc-parser "^3.0.0" + rollup-plugin-livereload@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz#4747fa292a2cceb0c972c573d71b3d66b4252b37" @@ -10443,17 +9901,6 @@ rollup-plugin-terser@^7.0.2: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup-plugin-typescript2@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" - integrity sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ== - dependencies: - "@rollup/pluginutils" "^4.1.0" - find-cache-dir "^3.3.1" - fs-extra "8.1.0" - resolve "1.20.0" - tslib "2.1.0" - rollup-plugin-typescript2@^0.31.2: version "0.31.2" resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.2.tgz#463aa713a7e2bf85b92860094b9f7fb274c5a4d8" @@ -10499,6 +9946,13 @@ rollup@^2.68.0: optionalDependencies: fsevents "~2.3.2" +rollup@^2.71.1: + version "2.71.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.71.1.tgz#82b259af7733dfd1224a8171013aaaad02971a22" + integrity sha512-lMZk3XfUBGjrrZQpvPSoXcZSfKcJ2Bgn+Z0L1MoW2V8Wh7BVM+LOBJTPo16yul2MwL59cXedzW1ruq3rCjSRgw== + optionalDependencies: + fsevents "~2.3.2" + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11461,11 +10915,6 @@ ts-node@^7.0.1: source-map-support "^0.5.6" yn "^2.0.0" -tslib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -11625,7 +11074,7 @@ typescript@*: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== -typescript@^3.9.5, typescript@^3.9.7: +typescript@^3.9.7: version "3.9.10" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== @@ -11635,6 +11084,11 @@ typescript@^4.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== +typescript@^4.6.4: + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== + uglify-js@^3.1.4: version "3.14.1" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" @@ -11704,7 +11158,7 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -11941,14 +11395,6 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" -whatwg-url@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-9.1.0.tgz#1b112cf237d72cd64fa7882b9c3f6234a1c3050d" - integrity sha512-CQ0UcrPHyomtlOCot1TL77WyMIm/bCwrJ2D6AOKGwEczU9EpyoqAokfqrf/MioU9kHcMsmJZcg1egXix2KYEsA== - dependencies: - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -12091,11 +11537,6 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== -ws@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.1.0.tgz#75e5ec608f66d3d3934ec6dbc4ebc8a34a68638c" - integrity sha512-0UWlCD2s3RSclw8FN+D0zDTUyMO+1kHwJQQJzkgUh16S8d3NYON0AKCEQPffE0ez4JyRFu76QDA9KR5bOG/7jw== - xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" From a43d4e425556950658c8757098cf6eb4b5ff54c2 Mon Sep 17 00:00:00 2001 From: yz-yu Date: Sat, 14 May 2022 14:52:26 +0800 Subject: [PATCH 009/201] Introduce benchmark tests and improve snapshot attributes traversing (#897) * housekeeping: refine test utils * setup benchmark tests * improve snapshot attributes loop perf --- packages/rrweb-snapshot/src/snapshot.ts | 11 +- packages/rrweb/src/record/mutation.ts | 2 +- .../rrweb/test/benchmark/dom-mutation.test.ts | 117 ++++++++++++++++++ packages/rrweb/test/e2e/webgl.test.ts | 42 ++----- .../test/html/benchmark-dom-mutation.html | 27 ++++ packages/rrweb/test/integration.test.ts | 31 +---- packages/rrweb/test/utils.ts | 55 ++++++-- packages/rrweb/tslint.json | 3 +- 8 files changed, 215 insertions(+), 73 deletions(-) create mode 100644 packages/rrweb/test/benchmark/dom-mutation.test.ts create mode 100644 packages/rrweb/test/html/benchmark-dom-mutation.html diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 9ccb67b52d..dded72ebd1 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -445,8 +445,15 @@ function serializeNode( ); const tagName = getValidTagName(n as HTMLElement); let attributes: attributes = {}; - for (const { name, value } of Array.from((n as HTMLElement).attributes)) { - attributes[name] = transformAttribute(doc, tagName, name, value); + const len = (n as HTMLElement).attributes.length; + for (let i = 0; i < len; i++) { + const attr = (n as HTMLElement).attributes[i]; + attributes[attr.name] = transformAttribute( + doc, + tagName, + attr.name, + attr.value, + ); } // remote css if (tagName === 'link' && inlineStylesheet) { diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 80718f2589..fb14f8eaeb 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -281,7 +281,7 @@ export default class MutationBuffer { if (parentId === -1 || nextId === -1) { return addList.addNode(n); } - let sn = serializeNodeWithId(n, { + const sn = serializeNodeWithId(n, { doc: this.doc, mirror: this.mirror, blockClass: this.blockClass, diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts new file mode 100644 index 0000000000..d2883ad0ae --- /dev/null +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -0,0 +1,117 @@ +// tslint:disable:no-console no-any +import * as fs from 'fs'; +import * as path from 'path'; +import type { eventWithTime, recordOptions } from '../../src/types'; +import { startServer, launchPuppeteer, replaceLast, ISuite } from '../utils'; + +function avg(v: number[]): number { + return v.reduce((prev, cur) => prev + cur, 0) / v.length; +} + +describe('benchmark: mutation observer', () => { + let code: ISuite['code']; + let page: ISuite['page']; + let browser: ISuite['browser']; + let server: ISuite['server']; + + beforeAll(async () => { + server = await startServer(); + browser = await launchPuppeteer({ + dumpio: true, + headless: true, + }); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.min.js'); + code = fs.readFileSync(bundlePath, 'utf8'); + }); + + afterEach(async () => { + await page.close(); + }); + + afterAll(async () => { + server.close(); + await browser.close(); + }); + + const getHtml = (fileName: string): string => { + const filePath = path.resolve(__dirname, `../html/${fileName}`); + const html = fs.readFileSync(filePath, 'utf8'); + return replaceLast( + html, + '', + ` + + + `, + ); + }; + + const suites: { + title: string; + html: string; + times?: number; // default to 5 + }[] = [ + { + title: 'create 1000x10 DOM nodes', + html: 'benchmark-dom-mutation.html', + times: 10, + }, + ]; + + for (const suite of suites) { + it(suite.title, async () => { + page = await browser.newPage(); + page.on('console', (message) => + console.log(`${message.type().toUpperCase()} ${message.text()}`), + ); + + const times = suite.times ?? 5; + const durations: number[] = []; + for (let i = 0; i < times; i++) { + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, suite.html)); + const duration = (await page.evaluate(() => { + return new Promise((resolve, reject) => { + let start = 0; + let lastEvent: eventWithTime | null; + const options: recordOptions = { + emit: (event) => { + // console.log(event.type, event.timestamp); + if (event.type !== 5 || event.data.tag !== 'FTAG') { + lastEvent = event; + return; + } + if (!lastEvent) { + reject('no events recorded'); + return; + } + resolve(lastEvent.timestamp - start); + }, + }; + const record = (window as any).rrweb.record; + record(options); + + (window as any).workload(); + + start = Date.now(); + setTimeout(() => { + record.addCustomEvent('FTAG', {}); + }, 0); + }); + })) as number; + durations.push(duration); + } + + console.table([ + { + ...suite, + duration: avg(durations), + durations: durations.join(', '), + }, + ]); + }); + } +}); diff --git a/packages/rrweb/test/e2e/webgl.test.ts b/packages/rrweb/test/e2e/webgl.test.ts index f77c92b193..1acdcc0037 100644 --- a/packages/rrweb/test/e2e/webgl.test.ts +++ b/packages/rrweb/test/e2e/webgl.test.ts @@ -1,4 +1,3 @@ -import type * as http from 'http'; import * as fs from 'fs'; import * as path from 'path'; import type * as puppeteer from 'puppeteer'; @@ -8,20 +7,13 @@ import { getServerURL, replaceLast, waitForRAF, + generateRecordSnippet, + ISuite, } from '../utils'; import type { recordOptions, eventWithTime } from '../../src/types'; import { toMatchImageSnapshot } from 'jest-image-snapshot'; expect.extend({ toMatchImageSnapshot }); -interface ISuite { - code: string; - browser: puppeteer.Browser; - server: http.Server; - page: puppeteer.Page; - events: eventWithTime[]; - serverURL: string; -} - describe('e2e webgl', () => { let code: ISuite['code']; let page: ISuite['page']; @@ -59,26 +51,14 @@ describe('e2e webgl', () => { ` `, ); }; - const fakeGoto = async (page: puppeteer.Page, url: string) => { + const fakeGoto = async (p: puppeteer.Page, url: string) => { const intercept = async (request: puppeteer.HTTPRequest) => { await request.respond({ status: 200, @@ -86,15 +66,15 @@ describe('e2e webgl', () => { body: ' ', // non-empty string or page will load indefinitely }); }; - await page.setRequestInterception(true); - page.on('request', intercept); - await page.goto(url); - page.off('request', intercept); - await page.setRequestInterception(false); + await p.setRequestInterception(true); + p.on('request', intercept); + await p.goto(url); + p.off('request', intercept); + await p.setRequestInterception(false); }; - const hideMouseAnimation = async (page: puppeteer.Page) => { - await page.addStyleTag({ + const hideMouseAnimation = async (p: puppeteer.Page) => { + await p.addStyleTag({ content: '.replayer-mouse-tail{display: none !important;}', }); }; diff --git a/packages/rrweb/test/html/benchmark-dom-mutation.html b/packages/rrweb/test/html/benchmark-dom-mutation.html new file mode 100644 index 0000000000..2921964c86 --- /dev/null +++ b/packages/rrweb/test/html/benchmark-dom-mutation.html @@ -0,0 +1,27 @@ + + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 4c92be6218..797d537346 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -1,6 +1,6 @@ +// tslint:disable:no-console import * as fs from 'fs'; import * as path from 'path'; -import type * as http from 'http'; import type * as puppeteer from 'puppeteer'; import { assertSnapshot, @@ -9,21 +9,12 @@ import { launchPuppeteer, waitForRAF, replaceLast, + generateRecordSnippet, + ISuite, } from './utils'; import { recordOptions, eventWithTime, EventType } from '../src/types'; import { visitSnapshot, NodeType } from 'rrweb-snapshot'; -interface ISuite { - server: http.Server; - serverURL: string; - code: string; - browser: puppeteer.Browser; -} - -interface IMimeType { - [key: string]: string; -} - describe('record integration tests', function (this: ISuite) { jest.setTimeout(10_000); @@ -40,19 +31,7 @@ describe('record integration tests', function (this: ISuite) { `, @@ -73,7 +52,7 @@ describe('record integration tests', function (this: ISuite) { const pluginsCode = [ path.resolve(__dirname, '../dist/plugins/console-record.min.js'), ] - .map((path) => fs.readFileSync(path, 'utf8')) + .map((p) => fs.readFileSync(p, 'utf8')) .join(); code = fs.readFileSync(bundlePath, 'utf8') + pluginsCode; }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 89f62c3fa4..2eb3a6a881 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -1,3 +1,4 @@ +// tslint:disable:no-console no-any import { NodeType } from 'rrweb-snapshot'; import { EventType, @@ -7,6 +8,7 @@ import { Optional, mouseInteractionData, event, + recordOptions, } from '../src/types'; import * as puppeteer from 'puppeteer'; import { format } from 'prettier'; @@ -15,15 +17,17 @@ import * as http from 'http'; import * as url from 'url'; import * as fs from 'fs'; -export async function launchPuppeteer() { +export async function launchPuppeteer( + options?: Parameters[0], +) { return await puppeteer.launch({ headless: process.env.PUPPETEER_HEADLESS ? true : false, defaultViewport: { width: 1920, height: 1080, }, - // devtools: true, args: ['--no-sandbox'], + ...options, }); } @@ -31,6 +35,15 @@ interface IMimeType { [key: string]: string; } +export interface ISuite { + server: http.Server; + serverURL: string; + code: string; + browser: puppeteer.Browser; + page: puppeteer.Page; + events: eventWithTime[]; +} + export const startServer = (defaultPort: number = 3030) => new Promise((resolve) => { const mimeType: IMimeType = { @@ -43,7 +56,7 @@ export const startServer = (defaultPort: number = 3030) => const sanitizePath = path .normalize(parsedUrl.pathname!) .replace(/^(\.\.[\/\\])+/, ''); - let pathname = path.join(__dirname, sanitizePath); + const pathname = path.join(__dirname, sanitizePath); try { const data = fs.readFileSync(pathname); @@ -179,9 +192,9 @@ function stringifyDomSnapshot(mhtml: string): string { .rewrite() // rewrite all links .spit(); // return all contents - const newResult: Array<{ filename: string; content: string }> = result.map( + const newResult: { filename: string; content: string }[] = result.map( (asset: { filename: string; content: string }) => { - let { filename, content } = asset; + const { filename, content } = asset; let res: string | undefined; if (filename.includes('frame')) { res = format(content, { @@ -226,7 +239,7 @@ export function stripBase64(events: eventWithTime[]) { if (!obj || typeof obj !== 'object') return obj; if (Array.isArray(obj)) return (obj.map((e) => walk(e)) as unknown) as T; const newObj: Partial = {}; - for (let prop in obj) { + for (const prop in obj) { const value = obj[prop]; if (prop === 'base64' && typeof value === 'string') { let index = base64Strings.indexOf(value); @@ -241,15 +254,15 @@ export function stripBase64(events: eventWithTime[]) { return newObj as T; } - return events.map((event) => { + return events.map((evt) => { if ( - event.type === EventType.IncrementalSnapshot && - event.data.source === IncrementalSource.CanvasMutation + evt.type === EventType.IncrementalSnapshot && + evt.data.source === IncrementalSource.CanvasMutation ) { - const newData = walk(event.data); - return { ...event, data: newData }; + const newData = walk(evt.data); + return { ...evt, data: newData }; } - return event; + return evt; }); } @@ -525,3 +538,21 @@ export async function waitForRAF(page: puppeteer.Page) { }); }); } + +export function generateRecordSnippet(options: recordOptions) { + return ` + window.snapshots = []; + rrweb.record({ + emit: event => { + window.snapshots.push(event); + }, + maskTextSelector: ${JSON.stringify(options.maskTextSelector)}, + maskAllInputs: ${options.maskAllInputs}, + maskInputOptions: ${JSON.stringify(options.maskAllInputs)}, + userTriggeredOnInput: ${options.userTriggeredOnInput}, + maskTextFn: ${options.maskTextFn}, + recordCanvas: ${options.recordCanvas}, + plugins: ${options.plugins} + }); + `; +} diff --git a/packages/rrweb/tslint.json b/packages/rrweb/tslint.json index ac74b2e5d7..c49e23ea37 100644 --- a/packages/rrweb/tslint.json +++ b/packages/rrweb/tslint.json @@ -24,7 +24,8 @@ "trailing-comma": false, "curly": false, "no-namespace": false, - "interface-name": false + "interface-name": false, + "forin": false }, "rulesDirectory": [] } From 1355917e1b54aa1d09123b47e4f887015e7a6f6f Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sun, 22 May 2022 03:59:42 +0200 Subject: [PATCH 010/201] Chore: Add issue/pr template and general housekeeping tools and docs (#900) * Add linting * Add issue templates and docs * Add root eslint config and remove tslint * Autofix lint issues --- .eslintrc.js | 21 + .github/ISSUE_TEMPLATE/bug_report.yml | 45 + .github/ISSUE_TEMPLATE/feature_request.yml | 48 + .github/config.yml | 19 + .markdownlint.yml | 239 + docs/development/coding-style.md | 52 + docs/styleguilde.md | 72 + package.json | 12 +- packages/rrdom/package.json | 6 +- packages/rrdom/src/diff.ts | 26 +- packages/rrdom/src/document-nodejs.ts | 26 +- packages/rrdom/src/document.ts | 25 +- packages/rrdom/src/style.ts | 2 +- packages/rrdom/src/virtual-dom.ts | 14 +- packages/rrweb-player/.eslintrc.json | 19 +- packages/rrweb-player/package.json | 10 +- packages/rrweb-player/src/Controller.svelte | 12 +- packages/rrweb-player/src/Player.svelte | 12 +- packages/rrweb-player/src/utils.ts | 2 +- packages/rrweb-snapshot/package.json | 4 +- packages/rrweb-snapshot/src/css.ts | 4 +- packages/rrweb-snapshot/src/rebuild.ts | 3 +- packages/rrweb-snapshot/src/snapshot.ts | 8 +- packages/rrweb-snapshot/tslint.json | 21 - packages/rrweb/package.json | 4 +- packages/rrweb/scripts/repl.js | 6 +- .../src/plugins/console/record/stringify.ts | 6 +- packages/rrweb/src/record/mutation.ts | 6 +- packages/rrweb/src/record/observer.ts | 4 +- .../record/observers/canvas/canvas-manager.ts | 4 +- .../src/record/observers/canvas/canvas.ts | 2 +- .../src/record/observers/canvas/webgl.ts | 6 +- packages/rrweb/src/replay/canvas/index.ts | 2 +- packages/rrweb/src/replay/canvas/webgl.ts | 2 +- packages/rrweb/src/replay/index.ts | 34 +- packages/rrweb/src/replay/machine.ts | 2 +- packages/rrweb/src/replay/smoothscroll.ts | 48 +- packages/rrweb/src/replay/timer.ts | 4 +- packages/rrweb/src/utils.ts | 12 +- packages/rrweb/tsconfig.json | 1 + packages/rrweb/tslint.json | 31 - tsconfig.eslint.json | 4 + tsconfig.json | 1 + yarn.lock | 3885 ++++++++--------- 44 files changed, 2511 insertions(+), 2255 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/config.yml create mode 100644 .markdownlint.yml create mode 100644 docs/development/coding-style.md create mode 100644 docs/styleguilde.md delete mode 100644 packages/rrweb-snapshot/tslint.json delete mode 100644 packages/rrweb/tslint.json create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..994f4229b1 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + tsconfigRootDir: __dirname, + project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], + }, + plugins: ['@typescript-eslint'], + rules: {}, +}; diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..571bc1800d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,45 @@ +name: Bug Report +description: Report an rrweb bug +title: '[Bug]: ' +labels: + - 'bug' +body: + - type: checkboxes + attributes: + label: Preflight Checklist + description: Please ensure you've completed all of the following. + options: + - label: I have searched the [issue tracker](https://www.github.com/rrweb-io/rrweb/issues) for a bug report that matches the one I want to file, without success. + required: true + - type: dropdown + attributes: + label: What package is this bug report for? + options: + - rrweb + - rrweb-snapshot + - rrdom + - rrweb-player + - Other (specify below) + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + attributes: + label: Actual Behavior + description: A clear description of what actually happens. + validations: + required: true + - type: input + attributes: + label: Testcase Gist URL + description: If you can reproduce the issue in a standalone test case, please save your recording events.json to a [GitHub gist](https://gist.github.com), paste that gist link into [rrwebdebug.com](https://rrwebdebug.com) and put the rrwebdebug.com URL here. This is **the best way** to ensure this issue is triaged quickly. + placeholder: https://rrwebdebug.com/... + - type: textarea + attributes: + label: Additional Information + description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..df2a0d7f2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,48 @@ +name: Feature Request +description: Suggest an idea for rrweb +title: '[Feature Request]: ' +labels: + - 'feature request' +body: + - type: checkboxes + attributes: + label: Preflight Checklist + description: Please ensure you've completed all of the following. + options: + - label: I have searched the [issue tracker](https://www.github.com/rrweb-io/rrweb/issues) for a feature request that matches the one I want to file, without success. + required: true + - type: dropdown + attributes: + label: What package is this feature request for? + options: + - rrweb + - rrweb-snapshot + - rrdom + - rrweb-player + - Other (specify below) + validations: + required: true + - type: textarea + attributes: + label: Problem Description + description: Please add a clear and concise description of the problem you are seeking to solve with this feature request. + validations: + required: true + - type: textarea + attributes: + label: Proposed Solution + description: Describe the solution you'd like in a clear and concise manner. + validations: + required: true + - type: textarea + attributes: + label: Alternatives Considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + - type: textarea + attributes: + label: Additional Information + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 0000000000..e1adc808e5 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,19 @@ +# Comment to be posted to on PRs from first time contributors in your repository +newPRWelcomeComment: | + 💖 Thanks for opening this pull request! 💖 + + Things that will help get your PR across the finish line: + + - Follow the TypeScript [coding style](https://github.com/rrweb-io/rrweb/blob/master/docs/development/coding-style.md). + - Run `yarn lint` locally to catch formatting errors earlier. + - Document any user-facing changes you've made following the [documentation styleguide](https://github.com/rrweb-io/rrweb/blob/master/blob/main/docs/styleguide.md). + - Include tests when adding/changing behavior. + - Include screenshots and animated GIFs whenever possible. + + We get a lot of pull requests on this repo, so please be patient and we will get back to you as soon as we can. + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge + +# Comment to be posted to on pull requests merged by a first time user +firstPRMergeComment: > + Congrats on merging your first pull request! 🎉🎉🎉Hallo diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000000..77cb411909 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,239 @@ +# markdownlint YAML configuration with all properties set to their default value + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time +MD001: true + +# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading +MD002: + # Heading level + level: 1 + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: 'consistent' + +# MD004/ul-style - Unordered list style +MD004: + # List style + style: 'consistent' + +# MD005/list-indent - Inconsistent indentation for list items at the same level +MD005: true + +# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line +MD006: true + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + # Number of spaces for each hard tab + spaces_per_tab: 1 + +# MD011/no-reversed-links - Reversed link syntax +MD011: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 80 + # Number of characters for headings + heading_line_length: 80 + # Number of characters for code blocks + code_block_line_length: 80 + # Include code blocks + code_blocks: true + # Include tables + tables: true + # Include headings + headings: true + # Include headings + headers: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD014/commands-show-output - Dollar signs used before commands without showing output +MD014: true + +# MD018/no-missing-space-atx - No space after hash on atx style heading +MD018: true + +# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading +MD019: true + +# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading +MD020: true + +# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading +MD021: true + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line +MD023: true + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: + # Only check sibling headings + allow_different_nesting: false + # Only check sibling headings + siblings_only: false + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: '.,;:!。,;:!' + +# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol +MD027: true + +# MD028/no-blanks-blockquote - Blank line inside blockquote +MD028: true + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: 'one_or_ordered' + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists - Lists should be surrounded by blank lines +MD032: true + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD034/no-bare-urls - Bare URL used +MD034: true + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: 'consistent' + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: '.,;:!?。,;:!?' + +# MD037/no-space-in-emphasis - Spaces inside emphasis markers +MD037: true + +# MD038/no-space-in-code - Spaces inside code span elements +MD038: true + +# MD039/no-space-in-links - Spaces inside link text +MD039: true + +# MD040/fenced-code-language - Fenced code blocks should have a language specified +MD040: true + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links - No empty links +MD042: true + +# MD043/required-headings/required-headers - Required heading structure +MD043: + # List of headings + headings: [] + # List of headings + headers: [] + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + +# MD045/no-alt-text - Images should have alternate text (alt text) +MD045: true + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: 'consistent' + +# MD047/single-trailing-newline - Files should end with a single newline character +MD047: true + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence style + style: 'consistent' + +# MD049/emphasis-style - Emphasis style should be consistent +MD049: + # Emphasis style should be consistent + style: 'consistent' + +# MD050/strong-style - Strong style should be consistent +MD050: + # Strong style should be consistent + style: 'consistent' diff --git a/docs/development/coding-style.md b/docs/development/coding-style.md new file mode 100644 index 0000000000..ef7556e3e1 --- /dev/null +++ b/docs/development/coding-style.md @@ -0,0 +1,52 @@ +# Coding Style + +These are the style guidelines for coding in Electron. + +You can run `yarn lint` to show any style issues detected by `tslint` and +`eslint`. + +## General Code + +- End files with a newline. +- Using a plain `return` when returning explicitly at the end of a function. + - Not `return null`, `return undefined`, `null` or `undefined` + +## Documentation + +- Write [remark](https://github.com/remarkjs/remark) markdown style. + + + +## TypeScript + +- Write [standard](https://www.npmjs.com/package/standard) JavaScript style. +- File names should be concatenated with `-` instead of `_`, e.g. + `file-name.js` rather than `file_name.js`, because in + [github/atom](https://github.com/github/atom) module names are usually in + the `module-name` form. This rule only applies to `.js` files. +- Use newer ES6/ES2015 syntax where appropriate + - [`const`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) + for requires and other constants. If the value is a primitive, use uppercase naming (eg `const NUMBER_OF_RETRIES = 5`). + - [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) + for defining variables + - [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) + instead of `function () { }` + - [Template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) + instead of string concatenation using `+` + +## Naming Things + +Electron APIs uses the same capitalization scheme as Node.js: + +- When the module itself is a class like `BrowserWindow`, use `PascalCase`. +- When the module is a set of APIs, like `globalShortcut`, use `camelCase`. +- When the API is a property of object, and it is complex enough to be in a + separate chapter like `win.webContents`, use `mixedCase`. +- For other non-module APIs, use natural titles, like ` Tag` or + `Process Object`. + +When creating a new API, it is preferred to use getters and setters instead of +jQuery's one-function style. For example, `.getText()` and `.setText(text)` +are preferred to `.text([text])`. There is a +[discussion](https://github.com/electron/electron/issues/46) on this. diff --git a/docs/styleguilde.md b/docs/styleguilde.md new file mode 100644 index 0000000000..db17dcdb6a --- /dev/null +++ b/docs/styleguilde.md @@ -0,0 +1,72 @@ +# rrweb Documentation Style Guide + +These are the guidelines for writing rrweb documentation. + +## Headings + +- Each page must have a single `#`-level title at the top. +- Chapters in the same page must have `##`-level headings. +- Sub-chapters need to increase the number of `#` in the heading according to + their nesting depth. +- The page's title must follow [APA title case][title-case]. +- All chapters must follow [APA sentence case][sentence-case]. + +Using `Quick Start` as example: + +```markdown +# Quick Start + +... + +## Replay + +... + +## Record + +... + +## Events + +... + +### Understanding Events + +... + +### Custom Events + +... +``` + +For API references, there are exceptions to this rule. + +## Markdown rules + +This repository uses the [`markdownlint`][markdownlint] package to enforce consistent +Markdown styling. For the exact rules, see the `.markdownlint.yaml` file in the root +folder. + +There are a few style guidelines that aren't covered by the linter rules: + + + +- Use `sh` instead of `cmd` in code blocks (due to the syntax highlighter). +- Keep line lengths between 80 and 100 characters if possible for readability + purposes. +- No nesting lists more than 2 levels (due to the markdown renderer). +- All `js` and `javascript` code blocks are linted with + [standard-markdown](https://www.npmjs.com/package/standard-markdown). +- For unordered lists, use asterisks instead of dashes. + +## Picking words + +- Use "will" over "would" when describing outcomes. + +## Documentation translations + +When adding something to an English doc file, please add a translation if possible, or a placeholder in the respective \*.zh-CN.md files. + +[title-case]: https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case +[sentence-case]: https://apastyle.apa.org/style-grammar-guidelines/capitalization/sentence-case +[markdownlint]: https://github.com/DavidAnson/markdownlint diff --git a/package.json b/package.json index 342273e6c3..eeb6276710 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,14 @@ "packages/rrdom" ], "devDependencies": { - "lerna": "^4.0.0" + "@typescript-eslint/eslint-plugin": "^5.25.0", + "@typescript-eslint/parser": "^5.25.0", + "concurrently": "^7.1.0", + "eslint": "^8.15.0", + "lerna": "^4.0.0", + "markdownlint": "^0.25.1", + "markdownlint-cli": "^0.31.1", + "typescript": "^4.6.4" }, "scripts": { "lerna": "lerna", @@ -29,7 +36,8 @@ "test": "yarn lerna run test", "test:watch": "yarn lerna run test:watch --parallel", "dev": "yarn lerna run dev --parallel", - "repl": "cd packages/rrweb && npm run repl" + "repl": "cd packages/rrweb && npm run repl", + "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'" }, "resolutions": { "**/jsdom/cssom": "^0.5.0" diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index bfe3459503..3acef09350 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -7,7 +7,8 @@ "bundle:es-only": "cross-env ES_ONLY=true rollup --config", "check-types": "tsc -noEmit", "test": "jest", - "prepublish": "npm run bundle" + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" }, "keywords": [ "rrweb", @@ -32,7 +33,10 @@ "@types/jest": "^27.4.1", "@types/nwsapi": "^2.2.2", "@types/puppeteer": "^5.4.4", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", "compare-versions": "^4.1.3", + "eslint": "^8.15.0", "jest": "^27.5.1", "puppeteer": "^9.1.1", "rollup": "^2.56.3", diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 26f2309147..8c493bcb7b 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -107,20 +107,21 @@ export function diff( let inputDataToApply = null, scrollDataToApply = null; switch (newTree.RRNodeType) { - case RRNodeType.Document: + case RRNodeType.Document: { const newRRDocument = newTree as IRRDocument; scrollDataToApply = (newRRDocument as RRDocument).scrollData; break; - case RRNodeType.Element: - const oldElement = (oldTree as Node) as HTMLElement; + } + case RRNodeType.Element: { + const oldElement = (oldTree ) as HTMLElement; const newRRElement = newTree as IRRElement; diffProps(oldElement, newRRElement, rrnodeMirror); scrollDataToApply = (newRRElement as RRElement).scrollData; inputDataToApply = (newRRElement as RRElement).inputData; switch (newRRElement.tagName) { case 'AUDIO': - case 'VIDEO': - const oldMediaElement = (oldTree as Node) as HTMLMediaElement; + case 'VIDEO': { + const oldMediaElement = (oldTree ) as HTMLMediaElement; const newMediaRRElement = newRRElement as RRMediaElement; if (newMediaRRElement.paused !== undefined) newMediaRRElement.paused @@ -133,13 +134,14 @@ export function diff( if (newMediaRRElement.currentTime !== undefined) oldMediaElement.currentTime = newMediaRRElement.currentTime; break; + } case 'CANVAS': (newTree as RRCanvasElement).canvasMutations.forEach( (canvasMutation) => replayer.applyCanvas( canvasMutation.event, canvasMutation.mutation, - (oldTree as Node) as HTMLCanvasElement, + (oldTree ) as HTMLCanvasElement, ), ); break; @@ -164,6 +166,7 @@ export function diff( ); } break; + } case RRNodeType.Text: case RRNodeType.Comment: case RRNodeType.CDATA: @@ -188,7 +191,7 @@ export function diff( // IFrame element doesn't have child nodes. if (newTree.nodeName === 'IFRAME') { - const oldContentDocument = ((oldTree as Node) as HTMLIFrameElement) + const oldContentDocument = ((oldTree ) as HTMLIFrameElement) .contentDocument; const newIFrameElement = newTree as RRIFrameElement; // If the iframe is cross-origin, the contentDocument will be null. @@ -316,10 +319,10 @@ function diffChildren( if ( replayer.mirror.getMeta(parentNode)?.type === RRNodeType.Document && replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element && - ((parentNode as Node) as Document).documentElement + ((parentNode ) as Document).documentElement ) { parentNode.removeChild( - ((parentNode as Node) as Document).documentElement, + ((parentNode ) as Document).documentElement, ); oldChildren[oldStartIndex] = undefined; oldStartNode = undefined; @@ -379,7 +382,7 @@ export function createOrGetNode( (rrNode as IRRDocumentType).systemId, ); break; - case RRNodeType.Element: + case RRNodeType.Element: { let tagName = (rrNode as IRRElement).tagName.toLowerCase(); tagName = SVGTagMap[tagName] || tagName; if (sn && 'isSVG' in sn && sn?.isSVG) { @@ -389,6 +392,7 @@ export function createOrGetNode( ); } else node = document.createElement((rrNode as IRRElement).tagName); break; + } case RRNodeType.Text: node = document.createTextNode((rrNode as IRRText).data); break; @@ -413,7 +417,7 @@ export function getNestedRule( return rule; } else { return getNestedRule( - ((rule as CSSGroupingRule).cssRules[position[1]] as CSSGroupingRule) + ((rule ).cssRules[position[1]] as CSSGroupingRule) .cssRules, position.slice(2), ); diff --git a/packages/rrdom/src/document-nodejs.ts b/packages/rrdom/src/document-nodejs.ts index f7523ffefc..3eb504fcca 100644 --- a/packages/rrdom/src/document-nodejs.ts +++ b/packages/rrdom/src/document-nodejs.ts @@ -70,7 +70,7 @@ export class RRDocument } get firstElementChild(): RRElement | null { - return this.documentElement as RRElement | null; + return this.documentElement; } appendChild(childNode: RRNode) { @@ -87,21 +87,19 @@ export class RRDocument getElementsByTagName(tagName: string): RRElement[] { if (this.documentElement) - return (this.documentElement as RRElement).getElementsByTagName(tagName); + return this.documentElement.getElementsByTagName(tagName); return []; } getElementsByClassName(className: string): RRElement[] { if (this.documentElement) - return (this.documentElement as RRElement).getElementsByClassName( - className, - ); + return this.documentElement.getElementsByClassName(className); return []; } getElementById(elementId: string): RRElement | null { if (this.documentElement) - return (this.documentElement as RRElement).getElementById(elementId); + return this.documentElement.getElementById(elementId); return null; } @@ -217,7 +215,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) { } getAttribute(name: string) { - let upperName = name && name.toLowerCase(); + const upperName = name && name.toLowerCase(); if (upperName in this.attributes) return this.attributes[upperName]; return null; } @@ -231,16 +229,16 @@ export class RRElement extends BaseRRElementImpl(RRNode) { } get firstElementChild(): RRElement | null { - for (let child of this.childNodes) + for (const child of this.childNodes) if (child.RRNodeType === RRNodeType.Element) return child as RRElement; return null; } get nextElementSibling(): RRElement | null { - let parentNode = this.parentNode; + const parentNode = this.parentNode; if (!parentNode) return null; const siblings = parentNode.childNodes; - let index = siblings.indexOf(this); + const index = siblings.indexOf(this); for (let i = index + 1; i < siblings.length; i++) if (siblings[i] instanceof RRElement) return siblings[i] as RRElement; return null; @@ -327,7 +325,7 @@ export class RRStyleElement extends RRElement { get sheet() { if (!this._sheet) { let result = ''; - for (let child of this.childNodes) + for (const child of this.childNodes) if (child.RRNodeType === RRNodeType.Text) result += (child as RRText).textContent; this._sheet = cssom.parse(result); @@ -337,9 +335,9 @@ export class RRStyleElement extends RRElement { } export class RRIFrameElement extends RRElement { - width: string = ''; - height: string = ''; - src: string = ''; + width = ''; + height = ''; + src = ''; contentDocument: RRDocument = new RRDocument(); contentWindow: RRWindow = new RRWindow(); diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index aec54d9a98..71d7f9f176 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -118,7 +118,9 @@ export interface IRRCDATASection extends IRRNode { data: string; } -type ConstrainedConstructor = new (...args: any[]) => T; +type ConstrainedConstructor> = new ( + ...args: any[] +) => T; /** * This is designed as an abstract class so it should never be instantiated. @@ -136,7 +138,9 @@ export class BaseRRNode implements IRRNode { public readonly nodeName: string; public readonly RRNodeType: RRNodeType; - constructor(...args: any[]) {} + constructor(...args: any[]) { + // + } public get firstChild(): IRRNode | null { return this.childNodes[0] || null; @@ -147,10 +151,10 @@ export class BaseRRNode implements IRRNode { } public get nextSibling(): IRRNode | null { - let parentNode = this.parentNode; + const parentNode = this.parentNode; if (!parentNode) return null; const siblings = parentNode.childNodes; - let index = siblings.indexOf(this); + const index = siblings.indexOf(this); return siblings[index + 1] || null; } @@ -295,7 +299,9 @@ export function BaseRRDocumentImpl< this.childNodes = []; } - public close() {} + public close() { + // + } /** * Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot). @@ -381,6 +387,7 @@ export function BaseRRDocumentImpl< export function BaseRRDocumentTypeImpl< RRNode extends ConstrainedConstructor >(RRNodeClass: RRNode) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRDocumentType extends RRNodeClass @@ -410,6 +417,7 @@ export function BaseRRDocumentTypeImpl< export function BaseRRElementImpl< RRNode extends ConstrainedConstructor >(RRNodeClass: RRNode) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRElement extends RRNodeClass implements IRRElement { public readonly nodeType: number = NodeType.ELEMENT_NODE; @@ -456,7 +464,7 @@ export function BaseRRElementImpl< public get style() { const style = (this.attributes.style - ? parseCSSText(this.attributes.style as string) + ? parseCSSText(this.attributes.style) : {}) as CSSStyleDeclaration; const hyphenateRE = /\B([A-Z])/g; style.setProperty = ( @@ -546,7 +554,7 @@ export function BaseRRElementImpl< toString() { let attributeString = ''; - for (let attribute in this.attributes) { + for (const attribute in this.attributes) { attributeString += `${attribute}="${this.attributes[attribute]}" `; } return `${this.tagName} ${attributeString}`; @@ -579,6 +587,7 @@ export function BaseRRMediaElementImpl< export function BaseRRTextImpl>( RRNodeClass: RRNode, ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRText extends RRNodeClass implements IRRText { public readonly nodeType: number = NodeType.TEXT_NODE; @@ -608,6 +617,7 @@ export function BaseRRTextImpl>( export function BaseRRCommentImpl< RRNode extends ConstrainedConstructor >(RRNodeClass: RRNode) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRComment extends RRNodeClass implements IRRComment { public readonly nodeType: number = NodeType.COMMENT_NODE; @@ -637,6 +647,7 @@ export function BaseRRCommentImpl< export function BaseRRCDATASectionImpl< RRNode extends ConstrainedConstructor >(RRNodeClass: RRNode) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRCDATASection extends RRNodeClass diff --git a/packages/rrdom/src/style.ts b/packages/rrdom/src/style.ts index a85f7598f0..22f63bdb98 100644 --- a/packages/rrdom/src/style.ts +++ b/packages/rrdom/src/style.ts @@ -17,7 +17,7 @@ export function parseCSSText(cssText: string): Record { export function toCSSText(style: Record): string { const properties = []; - for (let name in style) { + for (const name in style) { const value = style[name]; if (typeof value !== 'string') continue; const normalizedName = hyphenate(name); diff --git a/packages/rrdom/src/virtual-dom.ts b/packages/rrdom/src/virtual-dom.ts index 86f59f85b6..78f1dad05e 100644 --- a/packages/rrdom/src/virtual-dom.ts +++ b/packages/rrdom/src/virtual-dom.ts @@ -226,7 +226,7 @@ export function buildFromNode( } break; case NodeType.DOCUMENT_TYPE_NODE: - const documentType = (node as Node) as DocumentType; + const documentType = (node ) as DocumentType; rrNode = rrdom.createDocumentType( documentType.name, documentType.publicId, @@ -234,7 +234,7 @@ export function buildFromNode( ); break; case NodeType.ELEMENT_NODE: - const elementNode = (node as Node) as HTMLElement; + const elementNode = (node ) as HTMLElement; const tagName = getValidTagName(elementNode); rrNode = rrdom.createElement(tagName); const rrElement = rrNode as IRRElement; @@ -249,14 +249,14 @@ export function buildFromNode( */ break; case NodeType.TEXT_NODE: - rrNode = rrdom.createTextNode(((node as Node) as Text).textContent || ''); + rrNode = rrdom.createTextNode(((node ) as Text).textContent || ''); break; case NodeType.CDATA_SECTION_NODE: - rrNode = rrdom.createCDATASection(((node as Node) as CDATASection).data); + rrNode = rrdom.createCDATASection(((node ) as CDATASection).data); break; case NodeType.COMMENT_NODE: rrNode = rrdom.createComment( - ((node as Node) as Comment).textContent || '', + ((node ) as Comment).textContent || '', ); break; // if node is a shadow root @@ -316,9 +316,9 @@ export function buildFromDom( // if the node is a shadow dom if ( node.nodeType === NodeType.ELEMENT_NODE && - ((node as Node) as HTMLElement).shadowRoot + ((node ) as HTMLElement).shadowRoot ) - walk(((node as Node) as HTMLElement).shadowRoot!, rrNode); + walk(((node ) as HTMLElement).shadowRoot!, rrNode); node.childNodes.forEach((childNode) => walk(childNode, rrNode)); } } diff --git a/packages/rrweb-player/.eslintrc.json b/packages/rrweb-player/.eslintrc.json index f9317620a0..54fe6e7052 100644 --- a/packages/rrweb-player/.eslintrc.json +++ b/packages/rrweb-player/.eslintrc.json @@ -1,25 +1,22 @@ { - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2019, - "sourceType": "module" - }, + "extends": ["../../.eslintrc.js"], "rules": { "require-jsdoc": "off", "arrow-parens": "off", "object-curly-spacing": "off", "indent": "off" }, - "env": { - "es6": true, - "browser": true + "parserOptions": { + "extraFileExtensions": [".svelte"] }, - "plugins": ["svelte3", "@typescript-eslint"], + "plugins": ["svelte3"], "overrides": [ { "files": ["*.svelte"], "processor": "svelte3/svelte3" } - ] + ], + "settings": { + "svelte3/typescript": true + } } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 2824bb6170..e005b6e191 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -6,11 +6,8 @@ "@rollup/plugin-node-resolve": "^13.2.1", "@rollup/plugin-typescript": "^8.3.2", "@types/offscreencanvas": "^2019.6.4", - "@typescript-eslint/eslint-plugin": "^3.7.0", - "@typescript-eslint/parser": "^3.7.0", - "eslint": "^7.5.0", - "eslint-config-google": "^0.11.0", - "eslint-plugin-svelte3": "^2.7.3", + "eslint-config-google": "^0.14.0", + "eslint-plugin-svelte3": "^4.0.0", "postcss-easy-import": "^3.0.0", "rollup": "^2.71.1", "rollup-plugin-css-only": "^3.1.0", @@ -35,7 +32,8 @@ "prepublishOnly": "yarn build", "start": "sirv public", "validate": "svelte-check", - "prepublish": "yarn build" + "prepublish": "yarn build", + "lint": "yarn eslint src/**/*.ts" }, "description": "rrweb's replayer UI", "main": "lib/index.js", diff --git a/packages/rrweb-player/src/Controller.svelte b/packages/rrweb-player/src/Controller.svelte index 33cfac775a..f183e7d66e 100644 --- a/packages/rrweb-player/src/Controller.svelte +++ b/packages/rrweb-player/src/Controller.svelte @@ -186,20 +186,20 @@ }; export const triggerUpdateMeta = () => { - Promise.resolve().then(() => { + return Promise.resolve().then(() => { meta = replayer.getMetaData(); }) } onMount(() => { - playerState = replayer.service.state.value as typeof playerState; - speedState = replayer.speedService.state.value as typeof speedState; + playerState = replayer.service.state.value; + speedState = replayer.speedService.state.value ; replayer.on( 'state-change', (states: { player?: PlayerMachineState; speed?: SpeedMachineState }) => { const { player, speed } = states; if (player?.value && playerState !== player.value) { - playerState = player.value as typeof playerState; + playerState = player.value ; switch (playerState) { case 'playing': loopTimer(); @@ -212,7 +212,7 @@ } } if (speed?.value && speedState !== speed.value) { - speedState = speed.value as typeof speedState; + speedState = speed.value ; } }, ); @@ -338,7 +338,7 @@ class="rr-progress" class:disabled={speedState === 'skipping'} bind:this={progress} - on:click={(event) => handleProgressClick(event)}> + on:click={handleProgressClick}>
= {}; let replayer: Replayer; diff --git a/packages/rrweb-player/src/utils.ts b/packages/rrweb-player/src/utils.ts index b95f38e9c3..67a19396b7 100644 --- a/packages/rrweb-player/src/utils.ts +++ b/packages/rrweb-player/src/utils.ts @@ -28,7 +28,7 @@ function padZero(num: number, len = 2): string { const threshold = Math.pow(10, len - 1); if (num < threshold) { while (String(threshold).length > str.length) { - str = '0' + num; + str = `0${num}`; } } return str; diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index d66d3519ce..7f0465198f 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -11,7 +11,8 @@ "bundle:es-only": "cross-env ES_ONLY=true rollup --config", "dev": "yarn bundle:es-only --watch", "typings": "tsc -d --declarationDir typings", - "prepublish": "npm run typings && npm run bundle" + "prepublish": "npm run typings && npm run bundle", + "lint": "yarn eslint src" }, "repository": { "type": "git", @@ -55,7 +56,6 @@ "ts-jest": "^27.0.5", "ts-node": "^7.0.1", "tslib": "^1.9.3", - "tslint": "^4.5.1", "typescript": "^3.9.7" } } diff --git a/packages/rrweb-snapshot/src/css.ts b/packages/rrweb-snapshot/src/css.ts index c0b4001fd4..019a939658 100644 --- a/packages/rrweb-snapshot/src/css.ts +++ b/packages/rrweb-snapshot/src/css.ts @@ -242,7 +242,7 @@ export function parse(css: string, options: ParserOptions = {}) { if (lines) { lineno += lines.length; } - let i = str.lastIndexOf('\n'); + const i = str.lastIndexOf('\n'); column = i === -1 ? column + str.length : str.length - i; } @@ -457,7 +457,7 @@ export function parse(css: string, options: ParserOptions = {}) { const pos = position(); // prop - let propMatch = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); + const propMatch = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); if (!propMatch) { return; } diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 9e7440cba6..ca630e09b5 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -148,7 +148,8 @@ function buildNode( } let value = n.attributes[name]; if (tagName === 'option' && name === 'selected' && value === false) { - // legacy fix (TODO: if `value === false` can be generated for other attrs, should we also omit those other attrs from build?) + // legacy fix (TODO: if `value === false` can be generated for other attrs, + // should we also omit those other attrs from build ?) continue; } value = diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index dded72ebd1..90ca8f3765 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -154,7 +154,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { function collectCharacters(regEx: RegExp) { let chars: string; - let match = regEx.exec(attributeValue.substring(pos)); + const match = regEx.exec(attributeValue.substring(pos)); if (match) { chars = match[0]; pos += chars.length; @@ -163,7 +163,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { return ''; } - let output = []; + const output = []; while (true) { collectCharacters(SRCSET_COMMAS_OR_SPACES); if (pos >= attributeValue.length) { @@ -182,7 +182,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { url = absoluteToDoc(doc, url); let inParens = false; while (true) { - let c = attributeValue.charAt(pos); + const c = attributeValue.charAt(pos); if (c === '') { output.push((url + descriptorsStr).trim()); break; @@ -462,7 +462,7 @@ function serializeNode( }); let cssText: string | null = null; if (stylesheet) { - cssText = getCssRulesString(stylesheet as CSSStyleSheet); + cssText = getCssRulesString(stylesheet ); } if (cssText) { delete attributes.rel; diff --git a/packages/rrweb-snapshot/tslint.json b/packages/rrweb-snapshot/tslint.json deleted file mode 100644 index a153081c53..0000000000 --- a/packages/rrweb-snapshot/tslint.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended"], - "jsRules": {}, - "rules": { - "no-any": true, - "quotemark": [true, "single"], - "ordered-imports": false, - "object-literal-sort-keys": false, - "no-unused-variable": true, - "object-literal-key-quotes": false, - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], - "arrow-parens": false - }, - "rulesDirectory": [] -} diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 72b6ffff89..62da2925bc 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -14,7 +14,8 @@ "bundle": "rollup --config", "typings": "tsc -d --declarationDir typings", "check-types": "tsc -noEmit", - "prepublish": "npm run typings && npm run bundle" + "prepublish": "npm run typings && npm run bundle", + "lint": "yarn eslint src" }, "repository": { "type": "git", @@ -70,7 +71,6 @@ "ts-jest": "^27.1.3", "ts-node": "^10.7.0", "tslib": "^2.3.1", - "tslint": "^6.1.3", "typescript": "^4.6.2" }, "dependencies": { diff --git a/packages/rrweb/scripts/repl.js b/packages/rrweb/scripts/repl.js index 839b8d5ca6..9afdb70998 100644 --- a/packages/rrweb/scripts/repl.js +++ b/packages/rrweb/scripts/repl.js @@ -1,4 +1,4 @@ -/* tslint:disable: no-console */ +/* eslint:disable: no-console */ const fs = require('fs'); const path = require('path'); @@ -13,11 +13,11 @@ function getCode() { return fs.readFileSync(bundlePath, 'utf8'); } -(async () => { +void (async () => { const code = getCode(); let events = []; - start(); + await start(); const fakeGoto = async (page, url) => { const intercept = async (request) => { diff --git a/packages/rrweb/src/plugins/console/record/stringify.ts b/packages/rrweb/src/plugins/console/record/stringify.ts index e32069ff9f..75cc48013a 100644 --- a/packages/rrweb/src/plugins/console/record/stringify.ts +++ b/packages/rrweb/src/plugins/console/record/stringify.ts @@ -22,14 +22,14 @@ function pathToSelector(node: HTMLElement): string | '' { break; } name = name.toLowerCase(); - let parent = node.parentElement; + const parent = node.parentElement; - let domSiblings = []; + const domSiblings = []; if (parent.children && parent.children.length > 0) { // tslint:disable-next-line:prefer-for-of for (let i = 0; i < parent.children.length; i++) { - let sibling = parent.children[i]; + const sibling = parent.children[i]; if (sibling.localName && sibling.localName.toLowerCase) { if (sibling.localName.toLowerCase() === name) { domSiblings.push(sibling); diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index fb14f8eaeb..91272adda2 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -123,8 +123,8 @@ const moveKey = (id: number, parentId: number) => `${id}@${parentId}`; * controls behaviour of a MutationObserver */ export default class MutationBuffer { - private frozen: boolean = false; - private locked: boolean = false; + private frozen = false; + private locked = false; private texts: textCursor[] = []; private attributes: attributeCursor[] = []; @@ -597,7 +597,7 @@ export default class MutationBuffer { // if this node is blocked `serializeNode` will turn it into a placeholder element // but we have to remove it's children otherwise they will be added as placeholders too if (!isBlocked(n, this.blockClass)) - (n as Node).childNodes.forEach((childN) => this.genAdds(childN)); + (n ).childNodes.forEach((childN) => this.genAdds(childN)); }; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index fc1201ced6..fe5830b6b6 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -549,8 +549,8 @@ function initStyleSheetObserver( Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => { unmodifiedFunctions[typeKey] = { - insertRule: (type as GroupingCSSRuleTypes).prototype.insertRule, - deleteRule: (type as GroupingCSSRuleTypes).prototype.deleteRule, + insertRule: (type ).prototype.insertRule, + deleteRule: (type ).prototype.deleteRule, }; type.prototype.insertRule = function (rule: string, index?: number) { diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index 347be47375..187dd2d029 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -30,8 +30,8 @@ export class CanvasManager { private mutationCb: canvasMutationCallback; private resetObservers?: listenerHandler; - private frozen: boolean = false; - private locked: boolean = false; + private frozen = false; + private locked = false; public reset() { this.pendingCanvasMutations.clear(); diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index 74a9f8137a..7a5f264c5b 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -19,7 +19,7 @@ export default function initCanvasContextObserver( ) { if (!isBlocked(this, blockClass)) { if (!('__context' in this)) - (this as ICanvas).__context = contextType; + (this ).__context = contextType; } return original.apply(this, [contextType, ...args]); }; diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index f12a104db1..3a70500031 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -31,8 +31,8 @@ function patchGLPrototype( return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); saveWebGLVar(result, win, prototype); - if (!isBlocked(this.canvas as HTMLCanvasElement, blockClass)) { - const id = mirror.getId(this.canvas as HTMLCanvasElement); + if (!isBlocked(this.canvas , blockClass)) { + const id = mirror.getId(this.canvas ); const recordArgs = serializeArgs([...args], win, prototype); const mutation: canvasMutationWithType = { @@ -41,7 +41,7 @@ function patchGLPrototype( args: recordArgs, }; // TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement - cb(this.canvas as HTMLCanvasElement, mutation); + cb(this.canvas , mutation); } return result; diff --git a/packages/rrweb/src/replay/canvas/index.ts b/packages/rrweb/src/replay/canvas/index.ts index b192500a39..8d32b45544 100644 --- a/packages/rrweb/src/replay/canvas/index.ts +++ b/packages/rrweb/src/replay/canvas/index.ts @@ -24,7 +24,7 @@ export default async function canvasMutation({ errorHandler: Replayer['warnCanvasMutationFailed']; }): Promise { try { - let precomputedMutation: canvasMutationParam = + const precomputedMutation: canvasMutationParam = canvasEventMap.get(event) || mutation; const commands: canvasMutationCommand[] = diff --git a/packages/rrweb/src/replay/canvas/webgl.ts b/packages/rrweb/src/replay/canvas/webgl.ts index 4480cd9592..aa5f3191c1 100644 --- a/packages/rrweb/src/replay/canvas/webgl.ts +++ b/packages/rrweb/src/replay/canvas/webgl.ts @@ -12,7 +12,7 @@ function getContext( try { if (type === CanvasContext.WebGL) { return (target.getContext('webgl')! || - target.getContext('experimental-webgl')) as WebGLRenderingContext; + target.getContext('experimental-webgl')); } return target.getContext('webgl2')!; } catch (e) { diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 1e2ddcc0dd..e83b82c2e7 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -350,7 +350,7 @@ export class Replayer { this.speedService.send({ type: 'SET_SPEED', payload: { - speed: config.speed!, + speed: config.speed, }, }); } @@ -478,7 +478,7 @@ export class Replayer { private setupDom() { this.wrapper = document.createElement('div'); this.wrapper.classList.add('replayer-wrapper'); - this.config.root!.appendChild(this.wrapper); + this.config.root.appendChild(this.wrapper); this.mouse = document.createElement('div'); this.mouse.classList.add('replayer-mouse'); @@ -600,7 +600,7 @@ export class Replayer { } if (this.config.skipInactive && !this.nextUserInteractionEvent) { for (const _event of this.service.state.context.events) { - if (_event.timestamp! <= event.timestamp!) { + if (_event.timestamp <= event.timestamp) { continue; } if (this.isUserInteraction(_event)) { @@ -643,7 +643,7 @@ export class Replayer { this.service.send({ type: 'CAST_EVENT', payload: { event } }); // events are kept sorted by timestamp, check if this is the last event - let last_index = this.service.state.context.events.length - 1; + const last_index = this.service.state.context.events.length - 1; if (event === this.service.state.context.events[last_index]) { const finish = () => { if (last_index < this.service.state.context.events.length - 1) { @@ -675,7 +675,7 @@ export class Replayer { private rebuildFullSnapshot( event: fullSnapshotEvent & { timestamp: number }, - isSync: boolean = false, + isSync = false, ) { if (!this.iframe.contentDocument) { return console.warn('Looks like your replayer has been destroyed.'); @@ -731,7 +731,7 @@ export class Replayer { ); } if (this.usingVirtualDom) { - const styleEl = this.virtualDom.createElement('style') as RRStyleElement; + const styleEl = this.virtualDom.createElement('style') ; this.virtualDom.mirror.add( styleEl, getDefaultSN(styleEl, this.virtualDom.unserializedId), @@ -752,7 +752,7 @@ export class Replayer { head as HTMLHeadElement, ); for (let idx = 0; idx < injectStylesRules.length; idx++) { - (styleEl.sheet! as CSSStyleSheet).insertRule( + (styleEl.sheet! ).insertRule( injectStylesRules[idx], idx, ); @@ -1210,7 +1210,7 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - const styleSheet = ((target as Node) as HTMLStyleElement).sheet!; + const styleSheet = ((target ) as HTMLStyleElement).sheet!; d.adds?.forEach(({ rule, index: nestedIndex }) => { try { if (Array.isArray(nestedIndex)) { @@ -1383,7 +1383,7 @@ export class Replayer { type TNode = typeof mirror extends Mirror ? Node : RRNode; d.removes.forEach((mutation) => { - let target = mirror.getNode(mutation.id); + 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 @@ -1612,7 +1612,7 @@ export class Replayer { appendNode(mutation); }); - let startTime = Date.now(); + const startTime = Date.now(); while (queue.length) { // transform queue to resolve tree const resolveTrees = queueToResolveTrees(queue); @@ -1625,7 +1625,7 @@ export class Replayer { break; } for (const tree of resolveTrees) { - let parent = mirror.getNode(tree.value.parentId); + const parent = mirror.getNode(tree.value.parentId); if (!parent) { this.debug( 'Drop resolve tree since there is no parent for the root node.', @@ -1644,7 +1644,7 @@ export class Replayer { } uniqueTextMutations(d.texts).forEach((mutation) => { - let target = mirror.getNode(mutation.id); + 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 @@ -1664,7 +1664,7 @@ export class Replayer { } }); d.attributes.forEach((mutation) => { - let target = mirror.getNode(mutation.id); + 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 @@ -1692,9 +1692,9 @@ export class Replayer { } } } else if (attributeName === 'style') { - let styleValues = value as styleAttributeValue; + const styleValues = value ; const targetEl = target as HTMLElement | RRElement; - for (var s in styleValues) { + for (const s in styleValues) { if (styleValues[s] === false) { targetEl.style.removeProperty(s); } else if (styleValues[s] instanceof Array) { @@ -1772,7 +1772,7 @@ export class Replayer { const previousInMap = previousId && map[previousId]; const nextInMap = nextId && map[nextId]; if (previousInMap) { - const { node, mutation } = previousInMap as missingNode; + const { node, mutation } = previousInMap ; parent.insertBefore(node as Node & RRNode, target as Node & RRNode); delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; @@ -1781,7 +1781,7 @@ export class Replayer { } } if (nextInMap) { - const { node, mutation } = nextInMap as missingNode; + const { node, mutation } = nextInMap ; parent.insertBefore( node as Node & RRNode, target.nextSibling as Node & RRNode, diff --git a/packages/rrweb/src/replay/machine.ts b/packages/rrweb/src/replay/machine.ts index 8c5ca33f9e..4409dd1151 100644 --- a/packages/rrweb/src/replay/machine.ts +++ b/packages/rrweb/src/replay/machine.ts @@ -248,7 +248,7 @@ export function createPlayerService( let insertionIndex = -1; let start = 0; while (start <= end) { - let mid = Math.floor((start + end) / 2); + const mid = Math.floor((start + end) / 2); if (events[mid].timestamp <= event.timestamp) { start = mid + 1; } else { diff --git a/packages/rrweb/src/replay/smoothscroll.ts b/packages/rrweb/src/replay/smoothscroll.ts index fa2c0c81e8..5f03a0aaeb 100644 --- a/packages/rrweb/src/replay/smoothscroll.ts +++ b/packages/rrweb/src/replay/smoothscroll.ts @@ -15,11 +15,11 @@ export function polyfill(w: Window = window, d = document) { } // globals - var Element = w.HTMLElement || w.Element; - var SCROLL_TIME = 468; + const Element = w.HTMLElement || w.Element; + const SCROLL_TIME = 468; // object gathering original scroll methods - var original = { + const original = { scroll: w.scroll || w.scrollTo, scrollBy: w.scrollBy, elementScroll: Element.prototype.scroll || scrollElement, @@ -27,7 +27,7 @@ export function polyfill(w: Window = window, d = document) { }; // define timing method - var now = + const now = w.performance && w.performance.now ? w.performance.now.bind(w.performance) : Date.now; @@ -39,7 +39,7 @@ export function polyfill(w: Window = window, d = document) { * @returns {Boolean} */ function isMicrosoftBrowser(userAgent) { - var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/']; + const userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/']; return new RegExp(userAgentPatterns.join('|')).test(userAgent); } @@ -49,7 +49,7 @@ export function polyfill(w: Window = window, d = document) { * rounding up scrollHeight and scrollWidth causing false positives * on hasScrollableSpace */ - var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0; + const ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0; /** * changes scroll position inside an element @@ -130,7 +130,7 @@ export function polyfill(w: Window = window, d = document) { * @returns {Boolean} */ function canOverflow(el, axis) { - var overflowValue = w.getComputedStyle(el, null)['overflow' + axis]; + const overflowValue = w.getComputedStyle(el, null)['overflow' + axis]; return overflowValue === 'auto' || overflowValue === 'scroll'; } @@ -143,8 +143,8 @@ export function polyfill(w: Window = window, d = document) { * @returns {Boolean} */ function isScrollable(el) { - var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y'); - var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X'); + const isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y'); + const isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X'); return isScrollableY || isScrollableX; } @@ -170,11 +170,11 @@ export function polyfill(w: Window = window, d = document) { * @returns {undefined} */ function step(context) { - var time = now(); - var value; - var currentX; - var currentY; - var elapsed = (time - context.startTime) / SCROLL_TIME; + const time = now(); + let value; + let currentX; + let currentY; + let elapsed = (time - context.startTime) / SCROLL_TIME; // avoid elapsed times higher than one elapsed = elapsed > 1 ? 1 : elapsed; @@ -202,11 +202,11 @@ export function polyfill(w: Window = window, d = document) { * @returns {undefined} */ function smoothScroll(el, x, y) { - var scrollable; - var startX; - var startY; - var method; - var startTime = now(); + let scrollable; + let startX; + let startY; + let method; + const startTime = now(); // define scroll context if (el === d.body) { @@ -342,8 +342,8 @@ export function polyfill(w: Window = window, d = document) { return; } - var left = arguments[0].left; - var top = arguments[0].top; + const left = arguments[0].left; + const top = arguments[0].top; // LET THE SMOOTHNESS BEGIN! smoothScroll.call( @@ -396,9 +396,9 @@ export function polyfill(w: Window = window, d = document) { } // LET THE SMOOTHNESS BEGIN! - var scrollableParent = findScrollableParent(this); - var parentRects = scrollableParent.getBoundingClientRect(); - var clientRects = this.getBoundingClientRect(); + const scrollableParent = findScrollableParent(this); + const parentRects = scrollableParent.getBoundingClientRect(); + const clientRects = this.getBoundingClientRect(); if (scrollableParent !== d.body) { // reveal element inside parent diff --git a/packages/rrweb/src/replay/timer.ts b/packages/rrweb/src/replay/timer.ts index f6e7f82628..ee24aa2cce 100644 --- a/packages/rrweb/src/replay/timer.ts +++ b/packages/rrweb/src/replay/timer.ts @@ -6,7 +6,7 @@ import { } from '../types'; export class Timer { - public timeOffset: number = 0; + public timeOffset = 0; public speed: number; private actions: actionWithDelay[]; @@ -83,7 +83,7 @@ export class Timer { let start = 0; let end = this.actions.length - 1; while (start <= end) { - let mid = Math.floor((start + end) / 2); + const mid = Math.floor((start + end) / 2); if (this.actions[mid].delay < action.delay) { start = mid + 1; } else if (this.actions[mid].delay > action.delay) { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 5a1081ec52..0b546b5fe7 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -70,15 +70,14 @@ export function throttle( ) { let timeout: ReturnType | null = null; let previous = 0; - // tslint:disable-next-line: only-arrow-functions return function (arg: T) { - let now = Date.now(); + const now = Date.now(); if (!previous && options.leading === false) { previous = now; } - let remaining = wait - (now - previous); - let context = this; - let args = arguments; + const remaining = wait - (now - previous); + const context = this; + const args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); @@ -398,8 +397,7 @@ export function getNestedRule( return rule; } else { return getNestedRule( - ((rule as CSSGroupingRule).cssRules[position[1]] as CSSGroupingRule) - .cssRules, + (rule.cssRules[position[1]] as CSSGroupingRule).cssRules, position.slice(2), ); } diff --git a/packages/rrweb/tsconfig.json b/packages/rrweb/tsconfig.json index fe6c5a6848..57ec0c4979 100644 --- a/packages/rrweb/tsconfig.json +++ b/packages/rrweb/tsconfig.json @@ -16,6 +16,7 @@ "exclude": ["test"], "include": [ "src", + "scripts", "node_modules/@types/css-font-loading-module/index.d.ts", "node_modules/@types/jest-image-snapshot/index.d.ts" ] diff --git a/packages/rrweb/tslint.json b/packages/rrweb/tslint.json deleted file mode 100644 index c49e23ea37..0000000000 --- a/packages/rrweb/tslint.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended"], - "jsRules": {}, - "rules": { - "no-any": true, - "quotemark": [true, "single"], - "ordered-imports": false, - "object-literal-sort-keys": false, - "no-unused-variable": true, - "object-literal-key-quotes": false, - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], - "arrow-parens": false, - "only-arrow-functions": false, - "max-line-length": false, - "no-empty": false, - "max-classes-per-file": false, - "semicolon": false, - "trailing-comma": false, - "curly": false, - "no-namespace": false, - "interface-name": false, - "forin": false - }, - "rulesDirectory": [] -} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000000..778f63e52b --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": [".eslintrc.js"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/yarn.lock b/yarn.lock index a438248446..92bb7e13ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,40 +2,33 @@ # 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.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz" integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== dependencies: "@babel/highlight" "^7.14.5" "@babel/code-frame@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" "@babel/compat-data@^7.15.0": version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz" integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== "@babel/compat-data@^7.16.4": version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== -"@babel/core@^7.1.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": +"@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.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz" integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== dependencies: "@babel/code-frame" "^7.14.5" @@ -54,9 +47,9 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.12.3", "@babel/core@^7.8.0": +"@babel/core@^7.12.3": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz" integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA== dependencies: "@babel/code-frame" "^7.16.7" @@ -77,7 +70,7 @@ "@babel/generator@^7.15.4", "@babel/generator@^7.7.2": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz" integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== dependencies: "@babel/types" "^7.15.4" @@ -86,7 +79,7 @@ "@babel/generator@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.7.tgz#b42bf46a3079fa65e1544135f32e7958f048adbb" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz" integrity sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg== dependencies: "@babel/types" "^7.16.7" @@ -95,7 +88,7 @@ "@babel/helper-compilation-targets@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz" integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== dependencies: "@babel/compat-data" "^7.15.0" @@ -105,7 +98,7 @@ "@babel/helper-compilation-targets@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz" integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== dependencies: "@babel/compat-data" "^7.16.4" @@ -115,14 +108,14 @@ "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: "@babel/types" "^7.16.7" "@babel/helper-function-name@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz" integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== dependencies: "@babel/helper-get-function-arity" "^7.15.4" @@ -131,7 +124,7 @@ "@babel/helper-function-name@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz" integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: "@babel/helper-get-function-arity" "^7.16.7" @@ -140,56 +133,56 @@ "@babel/helper-get-function-arity@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz" integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== dependencies: "@babel/types" "^7.15.4" "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz" integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-hoist-variables@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz" integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== dependencies: "@babel/types" "^7.15.4" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz" integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: "@babel/types" "^7.16.7" "@babel/helper-member-expression-to-functions@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz" integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== dependencies: "@babel/types" "^7.15.4" "@babel/helper-module-imports@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz" integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== dependencies: "@babel/types" "^7.15.4" "@babel/helper-module-imports@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" "@babel/helper-module-transforms@^7.15.4": version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.7.tgz" integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== dependencies: "@babel/helper-module-imports" "^7.15.4" @@ -203,7 +196,7 @@ "@babel/helper-module-transforms@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz" integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== dependencies: "@babel/helper-environment-visitor" "^7.16.7" @@ -217,19 +210,19 @@ "@babel/helper-optimise-call-expression@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz" integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw== dependencies: "@babel/types" "^7.15.4" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== "@babel/helper-replace-supers@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz" integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== dependencies: "@babel/helper-member-expression-to-functions" "^7.15.4" @@ -239,60 +232,60 @@ "@babel/helper-simple-access@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz" integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg== dependencies: "@babel/types" "^7.15.4" "@babel/helper-simple-access@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz" integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== dependencies: "@babel/types" "^7.16.7" "@babel/helper-split-export-declaration@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz" integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== dependencies: "@babel/types" "^7.15.4" "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz" integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: "@babel/types" "^7.16.7" "@babel/helper-validator-identifier@^7.14.5": version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz" integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + 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-option@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== "@babel/helper-validator-option@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== "@babel/helpers@^7.15.4": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz" integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== dependencies: "@babel/template" "^7.15.4" @@ -301,16 +294,16 @@ "@babel/helpers@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz" integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== dependencies: "@babel/template" "^7.16.7" "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5": +"@babel/highlight@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== dependencies: "@babel/helper-validator-identifier" "^7.14.5" @@ -319,7 +312,7 @@ "@babel/highlight@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz" integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" @@ -328,108 +321,108 @@ "@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.yarnpkg.com/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz" integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== "@babel/parser@^7.14.7", "@babel/parser@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.7.tgz#d372dda9c89fcec340a82630a9f533f2fe15877e" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz" integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz" integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz" integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== dependencies: "@babel/code-frame" "^7.14.5" @@ -438,7 +431,7 @@ "@babel/template@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== dependencies: "@babel/code-frame" "^7.16.7" @@ -447,7 +440,7 @@ "@babel/traverse@^7.1.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.7.2": version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz" integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== dependencies: "@babel/code-frame" "^7.14.5" @@ -462,7 +455,7 @@ "@babel/traverse@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.7.tgz#dac01236a72c2560073658dd1a285fe4e0865d76" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz" integrity sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ== dependencies: "@babel/code-frame" "^7.16.7" @@ -478,7 +471,7 @@ "@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.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz" integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== dependencies: "@babel/helper-validator-identifier" "^7.14.9" @@ -486,7 +479,7 @@ "@babel/types@^7.16.7": version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.7.tgz#4ed19d51f840ed4bd5645be6ce40775fecf03159" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz" integrity sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" @@ -494,58 +487,58 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz" integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== "@cspotcode/source-map-support@0.7.0": version "0.7.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz" integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== 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== +"@eslint/eslintrc@^1.2.3": + version "1.2.3" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz" + integrity sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" + debug "^4.3.2" + espree "^9.3.2" globals "^13.9.0" - ignore "^4.0.6" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + 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== +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" minimatch "^3.0.4" -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== +"@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== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" - resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" + resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -556,12 +549,12 @@ "@istanbuljs/schema@^0.1.2": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.2.4.tgz#2f1a4bf82b9940065d4818fac271def99ec55e5e" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.2.4.tgz" integrity sha512-94znCKynPZpDpYHQ6esRJSc11AmONrVkBOBZiD7S+bSubHhrUfbS95EY5HIOxhm4PQO7cnvZkL3oJcY0oMA+Wg== dependencies: "@jest/types" "^27.2.4" @@ -573,7 +566,7 @@ "@jest/console@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz" integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: "@jest/types" "^27.5.1" @@ -585,7 +578,7 @@ "@jest/core@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.2.4.tgz#0b932da787d64848eab720dbb88e5b7a3f86e539" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.2.4.tgz" integrity sha512-UNQLyy+rXoojNm2MGlapgzWhZD1CT1zcHZQYeiD0xE7MtJfC19Q6J5D/Lm2l7i4V97T30usKDoEtjI8vKwWcLg== dependencies: "@jest/console" "^27.2.4" @@ -619,7 +612,7 @@ "@jest/core@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz" integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: "@jest/console" "^27.5.1" @@ -653,7 +646,7 @@ "@jest/environment@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.2.4.tgz#db3e60f7dd30ab950f6ce2d6d7293ed9a6b7cbcd" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.2.4.tgz" integrity sha512-wkuui5yr3SSQW0XD0Qm3TATUbL/WE3LDEM3ulC+RCQhMf2yxhci8x7svGkZ4ivJ6Pc94oOzpZ6cdHBAMSYd1ew== dependencies: "@jest/fake-timers" "^27.2.4" @@ -663,7 +656,7 @@ "@jest/environment@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: "@jest/fake-timers" "^27.5.1" @@ -673,7 +666,7 @@ "@jest/fake-timers@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.2.4.tgz#00df08bd60332bd59503cb5b6db21e4903785f86" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.2.4.tgz" integrity sha512-cs/TzvwWUM7kAA6Qm/890SK6JJ2pD5RfDNM3SSEom6BmdyV6OiWP1qf/pqo6ts6xwpcM36oN0wSEzcZWc6/B6w== dependencies: "@jest/types" "^27.2.4" @@ -685,7 +678,7 @@ "@jest/fake-timers@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: "@jest/types" "^27.5.1" @@ -697,7 +690,7 @@ "@jest/globals@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.2.4.tgz#0aeb22b011f8c8c4b8ff3b4dbd1ee0392fe0dd8a" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.2.4.tgz" integrity sha512-DRsRs5dh0i+fA9mGHylTU19+8fhzNJoEzrgsu+zgJoZth3x8/0juCQ8nVVdW1er4Cqifb/ET7/hACYVPD0dBEA== dependencies: "@jest/environment" "^27.2.4" @@ -706,7 +699,7 @@ "@jest/globals@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: "@jest/environment" "^27.5.1" @@ -715,7 +708,7 @@ "@jest/reporters@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.2.4.tgz#1482ff007f2e919d85c54b1563abb8b2ea2d5198" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.2.4.tgz" integrity sha512-LHeSdDnDZkDnJ8kvnjcqV8P1Yv/32yL4d4XfR5gBiy3xGO0onwll1QEbvtW96fIwhx2nejug0GTaEdNDoyr3fQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -745,7 +738,7 @@ "@jest/reporters@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz" integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -776,7 +769,7 @@ "@jest/source-map@^27.0.6": version "27.0.6" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.0.6.tgz" integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g== dependencies: callsites "^3.0.0" @@ -785,7 +778,7 @@ "@jest/source-map@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" @@ -794,7 +787,7 @@ "@jest/test-result@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.2.4.tgz#d1ca8298d168f1b0be834bfb543b1ac0294c05d7" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.2.4.tgz" integrity sha512-eU+PRo0+lIS01b0dTmMdVZ0TtcRSxEaYquZTRFMQz6CvsehGhx9bRzi9Zdw6VROviJyv7rstU+qAMX5pNBmnfQ== dependencies: "@jest/console" "^27.2.4" @@ -804,7 +797,7 @@ "@jest/test-result@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz" integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: "@jest/console" "^27.5.1" @@ -814,7 +807,7 @@ "@jest/test-sequencer@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.2.4.tgz#df66422a3e9e7440ce8b7498e255fa6b52c0bc03" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.2.4.tgz" integrity sha512-fpk5eknU3/DXE2QCCG1wv/a468+cfPo3Asu6d6yUtM9LOPh709ubZqrhuUOYfM8hXMrIpIdrv1CdCrWWabX0rQ== dependencies: "@jest/test-result" "^27.2.4" @@ -824,7 +817,7 @@ "@jest/test-sequencer@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz" integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: "@jest/test-result" "^27.5.1" @@ -834,7 +827,7 @@ "@jest/transform@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.2.4.tgz#2fe5b6836895f7a1b8bdec442c51e83943c62733" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.2.4.tgz" integrity sha512-n5FlX2TH0oQGwyVDKPxdJ5nI2sO7TJBFe3u3KaAtt7TOiV4yL+Y+rSFDl+Ic5MpbiA/eqXmLAQxjnBmWgS2rEA== dependencies: "@babel/core" "^7.1.0" @@ -855,7 +848,7 @@ "@jest/transform@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz" integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" @@ -876,7 +869,7 @@ "@jest/types@^27.2.4": version "27.2.4" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.4.tgz#2430042a66e00dc5b140c3636f4474d464c21ee8" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.2.4.tgz" integrity sha512-IDO2ezTxeMvQAHxzG/ZvEyA47q0aVfzT95rGFl7bZs/Go0aIucvfDbS2rmnoEdXxlLQhcolmoG/wvL/uKx4tKA== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -887,7 +880,7 @@ "@jest/types@^27.5.1": version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" @@ -898,7 +891,7 @@ "@lerna/add@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" + resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz" integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== dependencies: "@lerna/bootstrap" "4.0.0" @@ -914,7 +907,7 @@ "@lerna/bootstrap@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" + resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-4.0.0.tgz" integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== dependencies: "@lerna/command" "4.0.0" @@ -942,7 +935,7 @@ "@lerna/changed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" + resolved "https://registry.npmjs.org/@lerna/changed/-/changed-4.0.0.tgz" integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== dependencies: "@lerna/collect-updates" "4.0.0" @@ -952,7 +945,7 @@ "@lerna/check-working-tree@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" + resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz" integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== dependencies: "@lerna/collect-uncommitted" "4.0.0" @@ -961,7 +954,7 @@ "@lerna/child-process@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" + resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-4.0.0.tgz" integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== dependencies: chalk "^4.1.0" @@ -970,7 +963,7 @@ "@lerna/clean@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" + resolved "https://registry.npmjs.org/@lerna/clean/-/clean-4.0.0.tgz" integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== dependencies: "@lerna/command" "4.0.0" @@ -984,7 +977,7 @@ "@lerna/cli@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" + resolved "https://registry.npmjs.org/@lerna/cli/-/cli-4.0.0.tgz" integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== dependencies: "@lerna/global-options" "4.0.0" @@ -994,7 +987,7 @@ "@lerna/collect-uncommitted@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" + resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz" integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== dependencies: "@lerna/child-process" "4.0.0" @@ -1003,7 +996,7 @@ "@lerna/collect-updates@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" + resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-4.0.0.tgz" integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== dependencies: "@lerna/child-process" "4.0.0" @@ -1014,7 +1007,7 @@ "@lerna/command@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" + resolved "https://registry.npmjs.org/@lerna/command/-/command-4.0.0.tgz" integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== dependencies: "@lerna/child-process" "4.0.0" @@ -1030,7 +1023,7 @@ "@lerna/conventional-commits@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" + resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz" integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== dependencies: "@lerna/validation-error" "4.0.0" @@ -1047,7 +1040,7 @@ "@lerna/create-symlink@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" + resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-4.0.0.tgz" integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== dependencies: cmd-shim "^4.1.0" @@ -1056,7 +1049,7 @@ "@lerna/create@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" + resolved "https://registry.npmjs.org/@lerna/create/-/create-4.0.0.tgz" integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== dependencies: "@lerna/child-process" "4.0.0" @@ -1080,7 +1073,7 @@ "@lerna/describe-ref@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" + resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-4.0.0.tgz" integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1088,7 +1081,7 @@ "@lerna/diff@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" + resolved "https://registry.npmjs.org/@lerna/diff/-/diff-4.0.0.tgz" integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== dependencies: "@lerna/child-process" "4.0.0" @@ -1098,7 +1091,7 @@ "@lerna/exec@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" + resolved "https://registry.npmjs.org/@lerna/exec/-/exec-4.0.0.tgz" integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== dependencies: "@lerna/child-process" "4.0.0" @@ -1111,7 +1104,7 @@ "@lerna/filter-options@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" + resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-4.0.0.tgz" integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== dependencies: "@lerna/collect-updates" "4.0.0" @@ -1121,7 +1114,7 @@ "@lerna/filter-packages@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" + resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-4.0.0.tgz" integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== dependencies: "@lerna/validation-error" "4.0.0" @@ -1130,14 +1123,14 @@ "@lerna/get-npm-exec-opts@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" + resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz" integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== dependencies: npmlog "^4.1.2" "@lerna/get-packed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" + resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-4.0.0.tgz" integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== dependencies: fs-extra "^9.1.0" @@ -1146,7 +1139,7 @@ "@lerna/github-client@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" + resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-4.0.0.tgz" integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== dependencies: "@lerna/child-process" "4.0.0" @@ -1157,7 +1150,7 @@ "@lerna/gitlab-client@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" + resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz" integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== dependencies: node-fetch "^2.6.1" @@ -1166,12 +1159,12 @@ "@lerna/global-options@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" + resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-4.0.0.tgz" integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== "@lerna/has-npm-version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" + resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz" integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== dependencies: "@lerna/child-process" "4.0.0" @@ -1179,7 +1172,7 @@ "@lerna/import@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" + resolved "https://registry.npmjs.org/@lerna/import/-/import-4.0.0.tgz" integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== dependencies: "@lerna/child-process" "4.0.0" @@ -1193,7 +1186,7 @@ "@lerna/info@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" + resolved "https://registry.npmjs.org/@lerna/info/-/info-4.0.0.tgz" integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== dependencies: "@lerna/command" "4.0.0" @@ -1202,7 +1195,7 @@ "@lerna/init@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" + resolved "https://registry.npmjs.org/@lerna/init/-/init-4.0.0.tgz" integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== dependencies: "@lerna/child-process" "4.0.0" @@ -1213,7 +1206,7 @@ "@lerna/link@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" + resolved "https://registry.npmjs.org/@lerna/link/-/link-4.0.0.tgz" integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== dependencies: "@lerna/command" "4.0.0" @@ -1224,7 +1217,7 @@ "@lerna/list@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" + resolved "https://registry.npmjs.org/@lerna/list/-/list-4.0.0.tgz" integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== dependencies: "@lerna/command" "4.0.0" @@ -1234,7 +1227,7 @@ "@lerna/listable@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" + resolved "https://registry.npmjs.org/@lerna/listable/-/listable-4.0.0.tgz" integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== dependencies: "@lerna/query-graph" "4.0.0" @@ -1243,7 +1236,7 @@ "@lerna/log-packed@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" + resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-4.0.0.tgz" integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== dependencies: byte-size "^7.0.0" @@ -1253,7 +1246,7 @@ "@lerna/npm-conf@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" + resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-4.0.0.tgz" integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== dependencies: config-chain "^1.1.12" @@ -1261,7 +1254,7 @@ "@lerna/npm-dist-tag@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" + resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz" integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== dependencies: "@lerna/otplease" "4.0.0" @@ -1271,7 +1264,7 @@ "@lerna/npm-install@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" + resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-4.0.0.tgz" integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== dependencies: "@lerna/child-process" "4.0.0" @@ -1284,7 +1277,7 @@ "@lerna/npm-publish@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" + resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-4.0.0.tgz" integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== dependencies: "@lerna/otplease" "4.0.0" @@ -1298,7 +1291,7 @@ "@lerna/npm-run-script@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" + resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz" integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== dependencies: "@lerna/child-process" "4.0.0" @@ -1307,21 +1300,21 @@ "@lerna/otplease@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" + resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-4.0.0.tgz" integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== dependencies: "@lerna/prompt" "4.0.0" "@lerna/output@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" + resolved "https://registry.npmjs.org/@lerna/output/-/output-4.0.0.tgz" integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== dependencies: npmlog "^4.1.2" "@lerna/pack-directory@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" + resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-4.0.0.tgz" integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== dependencies: "@lerna/get-packed" "4.0.0" @@ -1334,7 +1327,7 @@ "@lerna/package-graph@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" + resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-4.0.0.tgz" integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== dependencies: "@lerna/prerelease-id-from-version" "4.0.0" @@ -1345,7 +1338,7 @@ "@lerna/package@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" + resolved "https://registry.npmjs.org/@lerna/package/-/package-4.0.0.tgz" integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== dependencies: load-json-file "^6.2.0" @@ -1354,14 +1347,14 @@ "@lerna/prerelease-id-from-version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" + resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz" integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== dependencies: semver "^7.3.4" "@lerna/profiler@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" + resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-4.0.0.tgz" integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== dependencies: fs-extra "^9.1.0" @@ -1370,7 +1363,7 @@ "@lerna/project@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" + resolved "https://registry.npmjs.org/@lerna/project/-/project-4.0.0.tgz" integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== dependencies: "@lerna/package" "4.0.0" @@ -1388,7 +1381,7 @@ "@lerna/prompt@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" + resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-4.0.0.tgz" integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== dependencies: inquirer "^7.3.3" @@ -1396,7 +1389,7 @@ "@lerna/publish@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" + resolved "https://registry.npmjs.org/@lerna/publish/-/publish-4.0.0.tgz" integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1430,21 +1423,21 @@ "@lerna/pulse-till-done@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" + resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz" integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== dependencies: npmlog "^4.1.2" "@lerna/query-graph@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" + resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-4.0.0.tgz" integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== dependencies: "@lerna/package-graph" "4.0.0" "@lerna/resolve-symlink@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" + resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz" integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== dependencies: fs-extra "^9.1.0" @@ -1453,7 +1446,7 @@ "@lerna/rimraf-dir@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" + resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz" integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== dependencies: "@lerna/child-process" "4.0.0" @@ -1463,7 +1456,7 @@ "@lerna/run-lifecycle@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" + resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz" integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== dependencies: "@lerna/npm-conf" "4.0.0" @@ -1472,7 +1465,7 @@ "@lerna/run-topologically@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" + resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-4.0.0.tgz" integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== dependencies: "@lerna/query-graph" "4.0.0" @@ -1480,7 +1473,7 @@ "@lerna/run@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" + resolved "https://registry.npmjs.org/@lerna/run/-/run-4.0.0.tgz" integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== dependencies: "@lerna/command" "4.0.0" @@ -1495,7 +1488,7 @@ "@lerna/symlink-binary@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" + resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz" integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1505,7 +1498,7 @@ "@lerna/symlink-dependencies@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" + resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz" integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== dependencies: "@lerna/create-symlink" "4.0.0" @@ -1517,19 +1510,19 @@ "@lerna/timer@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" + resolved "https://registry.npmjs.org/@lerna/timer/-/timer-4.0.0.tgz" integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== "@lerna/validation-error@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" + resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-4.0.0.tgz" integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== dependencies: npmlog "^4.1.2" "@lerna/version@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" + resolved "https://registry.npmjs.org/@lerna/version/-/version-4.0.0.tgz" integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== dependencies: "@lerna/check-working-tree" "4.0.0" @@ -1561,7 +1554,7 @@ "@lerna/write-log-file@4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" + resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-4.0.0.tgz" integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== dependencies: npmlog "^4.1.2" @@ -1569,7 +1562,7 @@ "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -1577,12 +1570,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -1590,12 +1583,12 @@ "@npmcli/ci-detect@^1.0.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz" integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== "@npmcli/git@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" + resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== dependencies: "@npmcli/promise-spawn" "^1.3.2" @@ -1609,7 +1602,7 @@ "@npmcli/installed-package-contents@^1.0.6": version "1.0.7" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" + resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== dependencies: npm-bundled "^1.1.1" @@ -1617,7 +1610,7 @@ "@npmcli/move-file@^1.0.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== dependencies: mkdirp "^1.0.4" @@ -1625,19 +1618,19 @@ "@npmcli/node-gyp@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede" + resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz" integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz" integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== dependencies: infer-owner "^1.0.4" "@npmcli/run-script@^1.8.2": version "1.8.5" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.5.tgz#f250a0c5e1a08a792d775a315d0ff42fc3a51e1d" + resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz" integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== dependencies: "@npmcli/node-gyp" "^1.0.2" @@ -1648,14 +1641,14 @@ "@octokit/auth-token@^2.4.4": version "2.4.5" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz" integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== dependencies: "@octokit/types" "^6.0.3" "@octokit/core@^3.5.0": version "3.5.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== dependencies: "@octokit/auth-token" "^2.4.4" @@ -1668,7 +1661,7 @@ "@octokit/endpoint@^6.0.1": version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" @@ -1677,7 +1670,7 @@ "@octokit/graphql@^4.5.8": version "4.6.4" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.4.tgz#0c3f5bed440822182e972317122acb65d311a5ed" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.4.tgz" integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== dependencies: "@octokit/request" "^5.6.0" @@ -1686,29 +1679,29 @@ "@octokit/openapi-types@^9.5.0": version "9.7.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-9.7.0.tgz#9897cdefd629cd88af67b8dbe2e5fb19c63426b2" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz" integrity sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^2.6.2": version "2.15.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz#264189dd3ce881c6c33758824aac05a4002e056a" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz" integrity sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg== dependencies: "@octokit/types" "^6.24.0" "@octokit/plugin-request-log@^1.0.2": version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@5.7.0": version "5.7.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz#80b69452c17597738d4692c79829b72d9e72ccec" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz" integrity sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw== dependencies: "@octokit/types" "^6.24.0" @@ -1716,7 +1709,7 @@ "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" @@ -1725,7 +1718,7 @@ "@octokit/request@^5.6.0": version "5.6.1" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.1.tgz#f97aff075c37ab1d427c49082fefeef0dba2d8ce" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz" integrity sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ== dependencies: "@octokit/endpoint" "^6.0.1" @@ -1737,7 +1730,7 @@ "@octokit/rest@^18.1.0": version "18.9.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.9.0.tgz#e5cc23fa199a2bdeea9efbe6096f81d7d6156fe9" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz" integrity sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA== dependencies: "@octokit/core" "^3.5.0" @@ -1747,19 +1740,19 @@ "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.24.0": version "6.25.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.25.0.tgz#c8e37e69dbe7ce55ed98ee63f75054e7e808bf1a" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz" integrity sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q== dependencies: "@octokit/openapi-types" "^9.5.0" "@polka/url@^0.5.0": version "0.5.0" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31" + resolved "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz" integrity sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw== "@rollup/plugin-commonjs@^20.0.0": version "20.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz#3246872dcbcb18a54aaa6277a8c7d7f1b155b745" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz" integrity sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1772,7 +1765,7 @@ "@rollup/plugin-commonjs@^22.0.0": version "22.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.0.tgz#f4d87016e2fbf187a593ab9f46626fe05b59e8bd" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.0.tgz" integrity sha512-Ktvf2j+bAO+30awhbYoCaXpBcyPmJbaEUYClQns/+6SNCYFURbvBiNbWgHITEsIgDDWCDUclWRKEuf8cwZCFoQ== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1785,7 +1778,7 @@ "@rollup/plugin-node-resolve@^13.0.4": version "13.0.6" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz#29629070bb767567be8157f575cfa8f2b8e9ef77" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz" integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1797,7 +1790,7 @@ "@rollup/plugin-node-resolve@^13.1.3": version "13.1.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz" integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1809,7 +1802,7 @@ "@rollup/plugin-node-resolve@^13.2.1": version "13.2.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz#cdee815cf02c180ff0a42536ca67a8f67e299f84" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz" integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1821,7 +1814,7 @@ "@rollup/plugin-typescript@^8.2.5": version "8.2.5" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz#e0319761b2b5105615e5a0c371ae05bc2984b7de" + resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz" integrity sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1829,7 +1822,7 @@ "@rollup/plugin-typescript@^8.3.2": version "8.3.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz#e1b719e2ed3e752bbc092001656c48378f2d15f0" + resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz" integrity sha512-MtgyR5LNHZr3GyN0tM7gNO9D0CS+Y+vflS4v/PHmrX17JCkHUYKvQ5jN5o3cz1YKllM3duXUqu3yOHwMPUxhDg== dependencies: "@rollup/pluginutils" "^3.1.0" @@ -1837,7 +1830,7 @@ "@rollup/pluginutils@4": version "4.1.1" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.1.tgz" integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ== dependencies: estree-walker "^2.0.1" @@ -1845,7 +1838,7 @@ "@rollup/pluginutils@^3.1.0": version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== dependencies: "@types/estree" "0.0.39" @@ -1854,7 +1847,7 @@ "@rollup/pluginutils@^4.1.1": version "4.2.1" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz" integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== dependencies: estree-walker "^2.0.1" @@ -1862,7 +1855,7 @@ "@rollup/pluginutils@^4.1.2": version "4.1.2" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz" integrity sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ== dependencies: estree-walker "^2.0.1" @@ -1870,51 +1863,51 @@ "@sinonjs/commons@^1.7.0": version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^8.0.1": version "8.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz#1c1c9a91419f804e59ae8df316a07dd1c3a76b94" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz" integrity sha512-AU7kwFxreVd6OAXcAFlKSmZquiRUU0FvYm44k1Y1QbK7Co4m0aqfGMhjykIeQp/H6rcl+nFmj0zfdUcGVs9Dew== dependencies: "@sinonjs/commons" "^1.7.0" "@tootallnate/once@1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== "@tsconfig/node10@^1.0.7": version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@tsconfig/svelte@^1.0.0": version "1.0.13" - resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-1.0.13.tgz#2fa34376627192c0d643ce54964915e2bd3a58e4" + resolved "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-1.0.13.tgz" integrity sha512-5lYJP45Xllo4yE/RUBccBT32eBlRDbqN8r1/MIvQbKxW3aFqaYPCNgm8D5V20X4ShHcwvYWNlKg3liDh1MlBoA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": version "7.1.16" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.16.tgz#bc12c74b7d65e82d29876b5d0baf5c625ac58702" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz" integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== dependencies: "@babel/parser" "^7.1.0" @@ -1925,14 +1918,14 @@ "@types/babel__generator@*": version "7.6.3" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz" integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== dependencies: "@babel/parser" "^7.1.0" @@ -1940,56 +1933,51 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.14.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== dependencies: "@babel/types" "^7.3.0" "@types/chai@^4.1.4", "@types/chai@^4.1.6": version "4.2.21" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.21.tgz#9f35a5643129df132cf3b5c1ec64046ea1af0650" + resolved "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz" integrity sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg== "@types/css-font-loading-module@0.0.7": version "0.0.7" - resolved "https://registry.yarnpkg.com/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz#2f98ede46acc0975de85c0b7b0ebe06041d24601" + resolved "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz" integrity sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q== "@types/cssom@^0.4.1": version "0.4.1" - resolved "https://registry.yarnpkg.com/@types/cssom/-/cssom-0.4.1.tgz#fb64e145b425bd6c1b0ed78ebd66ba43b6e088ab" + resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz" integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ== "@types/cssstyle@^2.2.1": version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/cssstyle/-/cssstyle-2.2.1.tgz#fa010824006ff47af94a6b9baf9759e031815347" + resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz" integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ== -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - "@types/estree@*": version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== "@types/estree@0.0.39": version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== "@types/graceful-fs@^4.1.2": version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== dependencies: "@types/node" "*" "@types/inquirer@0.0.43": version "0.0.43" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.43.tgz#1eb0bbb4648e6cc568bd396c1e989f620ad01273" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-0.0.43.tgz" integrity sha512-xgyfKZVMFqE8aIKy1xfFVsX2MxyXUNgjgmbF6dRbR3sL+ZM5K4ka/9L4mmTwX8eTeVYtduyXu0gUVwVJa1HbNw== dependencies: "@types/rx" "*" @@ -1997,26 +1985,26 @@ "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" "@types/jest-image-snapshot@^4.3.1": version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/jest-image-snapshot/-/jest-image-snapshot-4.3.1.tgz#1382e9e155d6e29af0a81efce1056aaba92110c9" + resolved "https://registry.npmjs.org/@types/jest-image-snapshot/-/jest-image-snapshot-4.3.1.tgz" integrity sha512-WDdUruGF14C53axe/mNDgQP2YIhtcwXrwmmVP8eOGyfNTVD+FbxWjWR7RTU+lzEy4K6V6+z7nkVDm/auI/r3xQ== dependencies: "@types/jest" "*" @@ -2025,7 +2013,7 @@ "@types/jest@*", "@types/jest@^27.0.2": version "27.0.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.2.tgz#ac383c4d4aaddd29bbf2b916d8d105c304a5fcd7" + resolved "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz" integrity sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA== dependencies: jest-diff "^27.0.0" @@ -2033,7 +2021,7 @@ "@types/jest@^27.4.1": version "27.4.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" + resolved "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz" integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== dependencies: jest-matcher-utils "^27.0.0" @@ -2041,194 +2029,194 @@ "@types/jsdom@^16.2.4": version "16.2.13" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f" + resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.13.tgz" integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw== dependencies: "@types/node" "*" "@types/parse5" "*" "@types/tough-cookie" "*" -"@types/json-schema@^7.0.3": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" - integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/minimatch@^3.0.3": version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/minimist@^1.2.0": version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== "@types/node@*": version "16.4.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272" + resolved "https://registry.npmjs.org/@types/node/-/node-16.4.0.tgz" integrity sha512-HrJuE7Mlqcjj+00JqMWpZ3tY8w7EUd+S0U3L1+PQSWiXZbOgyQDvi+ogoUxaHApPJq5diKxYBQwA3iIlNcPqOg== "@types/node@^10.11.3": version "10.17.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^17.0.21": version "17.0.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" + resolved "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz" integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/nwsapi@^2.2.2": version "2.2.2" - resolved "https://registry.yarnpkg.com/@types/nwsapi/-/nwsapi-2.2.2.tgz#1b1dccfc38b2b7e1b9ea71d5285796878375e862" + resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz" integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g== "@types/offscreencanvas@^2019.6.4": version "2019.6.4" - resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz#64f6d120b53925028299c744fcdd32d2cd525963" + resolved "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz" integrity sha512-u8SAgdZ8ROtkTF+mfZGOscl0or6BSj9A4g37e6nvxDc+YB/oDut0wHkK2PBBiC2bNR8TS0CPV+1gAk4fNisr1Q== "@types/parse-json@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/parse5@*": version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.1.tgz#f8ae4fbcd2b9ba4ff934698e28778961f9cb22ca" + resolved "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.1.tgz" integrity sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA== "@types/pixelmatch@*": version "5.2.4" - resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.4.tgz#ca145cc5ede1388c71c68edf2d1f5190e5ddd0f6" + resolved "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.4.tgz" integrity sha512-HDaSHIAv9kwpMN7zlmwfTv6gax0PiporJOipcrGsVNF3Ba+kryOZc0Pio5pn6NhisgWr7TaajlPEKTbTAypIBQ== dependencies: "@types/node" "*" "@types/prettier@^2.1.5": version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.1.tgz#e1303048d5389563e130f5bdd89d37a99acb75eb" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz" integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== "@types/prettier@^2.3.2": version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz" integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== "@types/pug@^2.0.4": version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.5.tgz#69bc700934dd473c7ab97270bd2dbacefe562231" + resolved "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz" integrity sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA== "@types/puppeteer@^1.12.4": version "1.20.8" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.20.8.tgz#fadbf64f7ac497e9248297beb6ed6a01705c0918" + resolved "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-1.20.8.tgz" integrity sha512-yJZzz9NeEmTxRGaZzUxUtBIEAoVXTtAx40mG8K0eDPwEeWyuxXKC7Lredxs6uNcgbvMDc8xzYy4v54jbbpoqrg== dependencies: "@types/node" "*" "@types/puppeteer@^5.4.4": version "5.4.5" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.5.tgz#154e3850a77bfd3967f036680de8ddc88eb3a12b" + resolved "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.5.tgz" integrity sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA== dependencies: "@types/node" "*" "@types/q@^1.5.1": version "1.5.5" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" + resolved "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== "@types/resolve@1.17.1": version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== dependencies: "@types/node" "*" "@types/rx-core-binding@*": version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz#d969d32f15a62b89e2862c17b3ee78fe329818d3" + resolved "https://registry.npmjs.org/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz" integrity sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ== dependencies: "@types/rx-core" "*" "@types/rx-core@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-core/-/rx-core-4.0.3.tgz#0b3354b1238cedbe2b74f6326f139dbc7a591d60" + resolved "https://registry.npmjs.org/@types/rx-core/-/rx-core-4.0.3.tgz" integrity sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA= "@types/rx-lite-aggregates@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz#6efb2b7f3d5f07183a1cb2bd4b1371d7073384c2" + resolved "https://registry.npmjs.org/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz" integrity sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg== dependencies: "@types/rx-lite" "*" "@types/rx-lite-async@*": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz#27fbf0caeff029f41e2d2aae638b05e91ceb600c" + resolved "https://registry.npmjs.org/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz" integrity sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw== dependencies: "@types/rx-lite" "*" "@types/rx-lite-backpressure@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz#05abb19bdf87cc740196c355e5d0b37bb50b5d56" + resolved "https://registry.npmjs.org/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz" integrity sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA== dependencies: "@types/rx-lite" "*" "@types/rx-lite-coincidence@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz#80bd69acc4054a15cdc1638e2dc8843498cd85c0" + resolved "https://registry.npmjs.org/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz" integrity sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ== dependencies: "@types/rx-lite" "*" "@types/rx-lite-experimental@*": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz#c532f5cbdf3f2c15da16ded8930d1b2984023cbd" + resolved "https://registry.npmjs.org/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz" integrity sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0= dependencies: "@types/rx-lite" "*" "@types/rx-lite-joinpatterns@*": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz#f70fe370518a8432f29158cc92ffb56b4e4afc3e" + resolved "https://registry.npmjs.org/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz" integrity sha1-9w/jcFGKhDLykVjMkv+1a05K/D4= dependencies: "@types/rx-lite" "*" "@types/rx-lite-testing@*": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz#21b19d11f4dfd6ffef5a9d1648e9c8879bfe21e9" + resolved "https://registry.npmjs.org/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz" integrity sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek= dependencies: "@types/rx-lite-virtualtime" "*" "@types/rx-lite-time@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz#0eda65474570237598f3448b845d2696f2dbb1c4" + resolved "https://registry.npmjs.org/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz" integrity sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw== dependencies: "@types/rx-lite" "*" "@types/rx-lite-virtualtime@*": version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz#4b30cacd0fe2e53af29f04f7438584c7d3959537" + resolved "https://registry.npmjs.org/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz" integrity sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg== dependencies: "@types/rx-lite" "*" "@types/rx-lite@*": version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/rx-lite/-/rx-lite-4.0.6.tgz#3c02921c4244074234f26b772241bcc20c18c253" + resolved "https://registry.npmjs.org/@types/rx-lite/-/rx-lite-4.0.6.tgz" integrity sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w== dependencies: "@types/rx-core" "*" @@ -2236,7 +2224,7 @@ "@types/rx@*": version "4.1.2" - resolved "https://registry.yarnpkg.com/@types/rx/-/rx-4.1.2.tgz#a4061b3d72b03cf11a38d69e2022a17334c54dc0" + resolved "https://registry.npmjs.org/@types/rx/-/rx-4.1.2.tgz" integrity sha512-1r8ZaT26Nigq7o4UBGl+aXB2UMFUIdLPP/8bLIP0x3d0pZL46ybKKjhWKaJQWIkLl5QCLD0nK3qTOO1QkwdFaA== dependencies: "@types/rx-core" "*" @@ -2254,115 +2242,215 @@ "@types/sass@^1.16.0": version "1.16.1" - resolved "https://registry.yarnpkg.com/@types/sass/-/sass-1.16.1.tgz#cf465bd1fea486d0331f760db023de14daf4980d" + resolved "https://registry.npmjs.org/@types/sass/-/sass-1.16.1.tgz" integrity sha512-iZUcRrGuz/Tbg3loODpW7vrQJkUtpY2fFSf4ELqqkApcS2TkZ1msk7ie8iZPB86lDOP8QOTTmuvWjc5S0R9OjQ== dependencies: "@types/node" "*" "@types/stack-utils@^2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/through@*": version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" + resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz" integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== dependencies: "@types/node" "*" "@types/tough-cookie@*": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz" integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== "@types/yargs-parser@*": version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== "@types/yargs@^16.0.0": version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz" integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== dependencies: "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": version "2.9.2" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz" integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^3.7.0": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f" - integrity sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ== +"@typescript-eslint/eslint-plugin@^5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.23.0.tgz" + integrity sha512-hEcSmG4XodSLiAp1uxv/OQSGsDY6QN3TcRU32gANp+19wGE1QQZLRS8/GV58VRUoXhnkuJ3ZxNQ3T6Z6zM59DA== dependencies: - "@typescript-eslint/experimental-utils" "3.10.1" - debug "^4.1.1" + "@typescript-eslint/scope-manager" "5.23.0" + "@typescript-eslint/type-utils" "5.23.0" + "@typescript-eslint/utils" "5.23.0" + debug "^4.3.2" functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" - integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^3.7.0": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" - integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.10.1" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/types@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" - integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== - -"@typescript-eslint/typescript-estree@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" - integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== - dependencies: - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/visitor-keys" "3.10.1" - debug "^4.1.1" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" - integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== +"@typescript-eslint/eslint-plugin@^5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.25.0.tgz#e8ce050990e4d36cc200f2de71ca0d3eb5e77a31" + integrity sha512-icYrFnUzvm+LhW0QeJNKkezBu6tJs9p/53dpPLFH8zoM9w1tfaKzVurkPotEpAqQ8Vf8uaFyL5jHd0Vs6Z0ZQg== dependencies: - eslint-visitor-keys "^1.1.0" + "@typescript-eslint/scope-manager" "5.25.0" + "@typescript-eslint/type-utils" "5.25.0" + "@typescript-eslint/utils" "5.25.0" + debug "^4.3.4" + functional-red-black-tree "^1.0.1" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.23.0.tgz" + integrity sha512-V06cYUkqcGqpFjb8ttVgzNF53tgbB/KoQT/iB++DOIExKmzI9vBJKjZKt/6FuV9c+zrDsvJKbJ2DOCYwX91cbw== + dependencies: + "@typescript-eslint/scope-manager" "5.23.0" + "@typescript-eslint/types" "5.23.0" + "@typescript-eslint/typescript-estree" "5.23.0" + debug "^4.3.2" + +"@typescript-eslint/parser@^5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.25.0.tgz#fb533487147b4b9efd999a4d2da0b6c263b64f7f" + integrity sha512-r3hwrOWYbNKP1nTcIw/aZoH+8bBnh/Lh1iDHoFpyG4DnCpvEdctrSl6LOo19fZbzypjQMHdajolxs6VpYoChgA== + dependencies: + "@typescript-eslint/scope-manager" "5.25.0" + "@typescript-eslint/types" "5.25.0" + "@typescript-eslint/typescript-estree" "5.25.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.23.0.tgz" + integrity sha512-EhjaFELQHCRb5wTwlGsNMvzK9b8Oco4aYNleeDlNuL6qXWDF47ch4EhVNPh8Rdhf9tmqbN4sWDk/8g+Z/J8JVw== + dependencies: + "@typescript-eslint/types" "5.23.0" + "@typescript-eslint/visitor-keys" "5.23.0" + +"@typescript-eslint/scope-manager@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.25.0.tgz#e78f1484bca7e484c48782075219c82c6b77a09f" + integrity sha512-p4SKTFWj+2VpreUZ5xMQsBMDdQ9XdRvODKXN4EksyBjFp2YvQdLkyHqOffakYZPuWJUDNu3jVXtHALDyTv3cww== + dependencies: + "@typescript-eslint/types" "5.25.0" + "@typescript-eslint/visitor-keys" "5.25.0" + +"@typescript-eslint/type-utils@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.23.0.tgz" + integrity sha512-iuI05JsJl/SUnOTXA9f4oI+/4qS/Zcgk+s2ir+lRmXI+80D8GaGwoUqs4p+X+4AxDolPpEpVUdlEH4ADxFy4gw== + dependencies: + "@typescript-eslint/utils" "5.23.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/type-utils@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.25.0.tgz#5750d26a5db4c4d68d511611e0ada04e56f613bc" + integrity sha512-B6nb3GK3Gv1Rsb2pqalebe/RyQoyG/WDy9yhj8EE0Ikds4Xa8RR28nHz+wlt4tMZk5bnAr0f3oC8TuDAd5CPrw== + dependencies: + "@typescript-eslint/utils" "5.25.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.23.0.tgz" + integrity sha512-NfBsV/h4dir/8mJwdZz7JFibaKC3E/QdeMEDJhiAE3/eMkoniZ7MjbEMCGXw6MZnZDMN3G9S0mH/6WUIj91dmw== + +"@typescript-eslint/types@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.25.0.tgz#dee51b1855788b24a2eceeae54e4adb89b088dd8" + integrity sha512-7fWqfxr0KNHj75PFqlGX24gWjdV/FDBABXL5dyvBOWHpACGyveok8Uj4ipPX/1fGU63fBkzSIycEje4XsOxUFA== + +"@typescript-eslint/typescript-estree@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.23.0.tgz" + integrity sha512-xE9e0lrHhI647SlGMl+m+3E3CKPF1wzvvOEWnuE3CCjjT7UiRnDGJxmAcVKJIlFgK6DY9RB98eLr1OPigPEOGg== + dependencies: + "@typescript-eslint/types" "5.23.0" + "@typescript-eslint/visitor-keys" "5.23.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.25.0.tgz#a7ab40d32eb944e3fb5b4e3646e81b1bcdd63e00" + integrity sha512-MrPODKDych/oWs/71LCnuO7NyR681HuBly2uLnX3r5i4ME7q/yBqC4hW33kmxtuauLTM0OuBOhhkFaxCCOjEEw== + dependencies: + "@typescript-eslint/types" "5.25.0" + "@typescript-eslint/visitor-keys" "5.25.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.23.0.tgz" + integrity sha512-dbgaKN21drqpkbbedGMNPCtRPZo1IOUr5EI9Jrrh99r5UW5Q0dz46RKXeSBoPV+56R6dFKpbrdhgUNSJsDDRZA== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.23.0" + "@typescript-eslint/types" "5.23.0" + "@typescript-eslint/typescript-estree" "5.23.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/utils@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.25.0.tgz#272751fd737733294b4ab95e16c7f2d4a75c2049" + integrity sha512-qNC9bhnz/n9Kba3yI6HQgQdBLuxDoMgdjzdhSInZh6NaDnFpTUlwNGxplUFWfY260Ya0TRPvkg9dd57qxrJI9g== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.25.0" + "@typescript-eslint/types" "5.25.0" + "@typescript-eslint/typescript-estree" "5.25.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.23.0": + version "5.23.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.23.0.tgz" + integrity sha512-Vd4mFNchU62sJB8pX19ZSPog05B0Y0CE2UxAZPT5k4iqhRYjPnqyY3woMxCd0++t9OTqkgjST+1ydLBi7e2Fvg== + dependencies: + "@typescript-eslint/types" "5.23.0" + eslint-visitor-keys "^3.0.0" + +"@typescript-eslint/visitor-keys@5.25.0": + version "5.25.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.25.0.tgz#33aa5fdcc5cedb9f4c8828c6a019d58548d4474b" + integrity sha512-yd26vFgMsC4h2dgX4+LR+GeicSKIfUvZREFLf3DDjZPtqgLx5AJZr6TetMNwFP9hcKreTTeztQYBTNbNoOycwA== + dependencies: + "@typescript-eslint/types" "5.25.0" + eslint-visitor-keys "^3.3.0" "@xstate/fsm@^1.4.0": version "1.6.1" - resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.6.1.tgz#c92972b835540c4e3c5e14277f40dbcbdaee9571" + resolved "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.1.tgz" integrity sha512-xYKDNuPR36/fUK+jmhM+oauBmbdUAfuJKnDjg3/7NbN+Pj03TX7e94LXnzkwGgAR+U/HWoMqM5UPTuGIYfIx9g== "@yarn-tool/resolve-package@^1.0.40": version "1.0.45" - resolved "https://registry.yarnpkg.com/@yarn-tool/resolve-package/-/resolve-package-1.0.45.tgz#4d9716a67903f46a76c8691eff546dafe55bf66f" + resolved "https://registry.npmjs.org/@yarn-tool/resolve-package/-/resolve-package-1.0.45.tgz" integrity sha512-xnfY8JceApkSTliZtr7X6yl1wZYhGbRp0beBMi1OtmvTVTm/ZSt3881Fw1M3ZwhHqr7OEfl8828LJK2q62BvoQ== dependencies: pkg-dir "< 6 >= 5" @@ -2371,7 +2459,7 @@ JSONStream@^1.0.4: version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" @@ -2379,17 +2467,17 @@ JSONStream@^1.0.4: abab@^2.0.3, abab@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== accepts@~1.3.7: version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" @@ -2397,64 +2485,69 @@ accepts@~1.3.7: acorn-globals@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== dependencies: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^7.1.1: version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn-walk@^8.1.1: version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + 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.4.0: +acorn@^7.1.1: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== acorn@^8.4.1: version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.7.1: + version "8.7.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + add-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" + resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= agent-base@6, agent-base@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agent-base@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz" integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== dependencies: es6-promisify "^5.0.0" agentkeepalive@^4.1.3: version "4.1.4" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz" integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== dependencies: debug "^4.1.0" @@ -2463,7 +2556,7 @@ agentkeepalive@^4.1.3: aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -2471,7 +2564,7 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2479,97 +2572,70 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.6.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" - integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== - 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.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= - dependencies: - string-width "^2.0.0" - -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - ansi-escapes@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-regex@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" @@ -2577,17 +2643,17 @@ anymatch@^3.0.3, anymatch@~3.1.2: aproba@^1.0.3: version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== aproba@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== are-we-there-yet@~1.1.2: version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== dependencies: delegates "^1.0.0" @@ -2595,134 +2661,125 @@ are-we-there-yet@~1.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz" integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= dependencies: arr-flatten "^1.0.1" arr-flatten@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== array-differ@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-ify@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-union@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= dependencies: array-uniq "^1.0.1" array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array-uniq@^1.0.1: version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= arrify@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asap@^2.0.0: version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + 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.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== aws-sign2@~0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -babel-code-frame@^6.20.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - babel-jest@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.2.4.tgz#21ed6729d51bdd75470bbbf3c8b08d86209fb0dc" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.4.tgz" integrity sha512-f24OmxyWymk5jfgLdlCMu4fTs4ldxFBIdn5sJdhvGC1m08rSkJ5hYbWkNmfBSvE/DjhCVNSHXepxsI6THGfGsg== dependencies: "@jest/transform" "^27.2.4" @@ -2736,7 +2793,7 @@ babel-jest@^27.2.4: babel-jest@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: "@jest/transform" "^27.5.1" @@ -2750,7 +2807,7 @@ babel-jest@^27.5.1: babel-plugin-istanbul@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2761,7 +2818,7 @@ babel-plugin-istanbul@^6.0.0: babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2772,7 +2829,7 @@ babel-plugin-istanbul@^6.1.1: babel-plugin-jest-hoist@^27.2.0: version "27.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.2.0.tgz#79f37d43f7e5c4fdc4b2ca3e10cc6cf545626277" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.2.0.tgz" integrity sha512-TOux9khNKdi64mW+0OIhcmbAn75tTlzKhxmiNXevQaPbrBYK7YKjP1jl6NHTJ6XR5UgUrJbCnWlKVnJn29dfjw== dependencies: "@babel/template" "^7.3.3" @@ -2782,7 +2839,7 @@ babel-plugin-jest-hoist@^27.2.0: babel-plugin-jest-hoist@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz" integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" @@ -2792,7 +2849,7 @@ babel-plugin-jest-hoist@^27.5.1: babel-preset-current-node-syntax@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -2810,7 +2867,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^27.2.0: version "27.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.2.0.tgz#556bbbf340608fed5670ab0ea0c8ef2449fba885" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.2.0.tgz" integrity sha512-z7MgQ3peBwN5L5aCqBKnF6iqdlvZvFUQynEhu0J+X9nHLU72jO3iY331lcYrg+AssJ8q7xsv5/3AICzVmJ/wvg== dependencies: babel-plugin-jest-hoist "^27.2.0" @@ -2818,7 +2875,7 @@ babel-preset-jest@^27.2.0: babel-preset-jest@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz" integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: babel-plugin-jest-hoist "^27.5.1" @@ -2826,7 +2883,7 @@ babel-preset-jest@^27.5.1: babel-runtime@^6.26.0: version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= dependencies: core-js "^2.4.0" @@ -2834,7 +2891,7 @@ babel-runtime@^6.26.0: babel-types@^6.0.0: version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz" integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= dependencies: babel-runtime "^6.26.0" @@ -2844,44 +2901,44 @@ babel-types@^6.0.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-arraybuffer@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz#87bd13525626db4a9838e00a508c2b73efcf348c" + resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz" integrity sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA== base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bcrypt-pbkdf@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" before-after-hook@^2.2.0: version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bl@^4.0.3: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -2890,12 +2947,12 @@ bl@^4.0.3: bluebird@^3.5.2: version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== body-parser@1.19.0: version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" @@ -2911,25 +2968,12 @@ body-parser@1.19.0: boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -boxen@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -2937,7 +2981,7 @@ brace-expansion@^1.1.7: braces@^1.8.2: version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + resolved "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz" integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= dependencies: expand-range "^1.8.1" @@ -2946,26 +2990,26 @@ braces@^1.8.2: braces@^3.0.1, braces@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browser-resolve@^1.11.3: version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + resolved "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz" integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== dependencies: resolve "1.1.7" browserslist@^4.0.0: version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz" integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== dependencies: caniuse-lite "^1.0.30001219" @@ -2976,7 +3020,7 @@ browserslist@^4.0.0: browserslist@^4.16.6: version "4.17.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.3.tgz#2844cd6eebe14d12384b0122d217550160d2d624" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz" integrity sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ== dependencies: caniuse-lite "^1.0.30001264" @@ -2987,7 +3031,7 @@ browserslist@^4.16.6: browserslist@^4.17.5: version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz" integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== dependencies: caniuse-lite "^1.0.30001286" @@ -2998,31 +3042,31 @@ browserslist@^4.17.5: bs-logger@0.x: version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-crc32@~0.2.3: version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer@^5.2.1, buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -3030,37 +3074,37 @@ buffer@^5.2.1, buffer@^5.5.0: builtin-modules@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= builtin-modules@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz" integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== builtins@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= byline@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= byte-size@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" + resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== bytes@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^15.0.5, cacache@^15.2.0: version "15.2.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" + resolved "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz" integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== dependencies: "@npmcli/move-file" "^1.0.1" @@ -3083,7 +3127,7 @@ cacache@^15.0.5, cacache@^15.2.0: call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -3091,55 +3135,50 @@ call-bind@^1.0.0, call-bind@^1.0.2: caller-callsite@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= dependencies: callsites "^2.0.0" caller-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= dependencies: caller-callsite "^2.0.0" callsites@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^6.2.2: version "6.2.2" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: camelcase "^5.3.1" map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-api@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== dependencies: browserslist "^4.0.0" @@ -3149,32 +3188,27 @@ caniuse-api@^3.0.0: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219: version "1.0.30001246" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001246.tgz#fe17d9919f87124d6bb416ef7b325356d69dc76c" + 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.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3" + 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.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001297.tgz#ea7776ccc4992956582cae5b8fea127fbebde430" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001297.tgz" integrity sha512-6bbIbowYG8vFs/Lk4hU9jFt7NknGDleVAciK916tp6ft1j+D//ZwwL6LbF1wXMQ32DMSjeuUV8suhh6dlmFjcA== -capture-stack-trace@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== - caseless@~0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" @@ -3185,7 +3219,7 @@ chalk@^1.1.3: chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3194,7 +3228,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: chalk@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== dependencies: ansi-styles "^4.1.0" @@ -3202,7 +3236,7 @@ chalk@^4.0.0: chalk@^4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -3210,17 +3244,17 @@ chalk@^4.1.0: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== cheerio-select@^1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" + resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz" integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== dependencies: css-select "^4.1.3" @@ -3231,7 +3265,7 @@ cheerio-select@^1.5.0: cheerio@^1.0.0-rc.3: version "1.0.0-rc.10" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz" integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== dependencies: cheerio-select "^1.5.0" @@ -3244,7 +3278,7 @@ cheerio@^1.0.0-rc.3: chokidar@^3.4.1, chokidar@^3.5.0: version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== dependencies: anymatch "~3.1.2" @@ -3259,76 +3293,66 @@ chokidar@^3.4.1, chokidar@^3.5.0: chownr@^1.1.1, chownr@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chownr@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - ci-info@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.1.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz" integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== ci-info@^3.2.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cjs-module-lexer@^1.0.0: version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - cli-cursor@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-width@^2.0.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== cliui@^7.0.2: version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -3337,7 +3361,7 @@ cliui@^7.0.2: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -3346,24 +3370,24 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= cmd-shim@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd" + resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz" integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== dependencies: mkdirp-infer-owner "^2.0.0" co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= coa@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== dependencies: "@types/q" "^1.5.1" @@ -3372,41 +3396,41 @@ coa@^2.0.2: code-point-at@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collect-v8-coverage@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: version "1.8.2" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.8.2.tgz#08bd49fa5f3889c27b0c670052ed746dd7a671de" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.8.2.tgz" integrity sha512-w5ZkKRdLsc5NOYsmnpS2DpyRW71npwZGwbRpLrJTuqjfTs2Bhrba7UiV59IX9siBlCPl2pne5NtiwnVWUzvYFA== dependencies: color-name "^1.0.0" @@ -3414,7 +3438,7 @@ color-string@^1.6.0: color@^3.0.0: version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== dependencies: color-convert "^1.9.3" @@ -3422,17 +3446,12 @@ color@^3.0.0: colorette@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -colors@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - columnify@^1.5.4: version "1.5.4" - resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= dependencies: strip-ansi "^3.0.0" @@ -3440,24 +3459,29 @@ columnify@^1.5.4: combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^2.12.1, commander@^2.20.0: version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@~9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40" + integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw== + commondir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= compare-func@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== dependencies: array-ify "^1.0.0" @@ -3465,17 +3489,17 @@ compare-func@^2.0.0: compare-versions@^4.1.3: version "4.1.3" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" + resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz" integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^1.6.2: version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" @@ -3485,7 +3509,7 @@ concat-stream@^1.6.2: concat-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== dependencies: buffer-from "^1.0.0" @@ -3495,56 +3519,58 @@ concat-stream@^2.0.0: concat-with-sourcemaps@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + resolved "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz" integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== dependencies: source-map "^0.6.1" +concurrently@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.1.0.tgz#477b49b8cfc630bb491f9b02e9ed7fb7bff02942" + integrity sha512-Bz0tMlYKZRUDqJlNiF/OImojMB9ruKUz6GCfmhFnSapXgPe+3xzY4byqoKG9tUZ7L2PGEUjfLPOLfIX3labnmw== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.21" + rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + config-chain@^1.1.12: version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" proto-list "~1.2.1" -configstore@^3.0.0: - version "3.1.5" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.5.tgz#e9af331fadc14dabd544d3e7e76dc446a09a530f" - integrity sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA== - dependencies: - dot-prop "^4.2.1" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - console-clear@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/console-clear/-/console-clear-1.1.1.tgz#995e20cbfbf14dd792b672cde387bd128d674bf7" + resolved "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz" integrity sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ== console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= content-disposition@0.5.3: version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: version "5.0.12" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz#c979b8b921cbfe26402eb3da5bbfda02d865a2b9" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz" integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== dependencies: compare-func "^2.0.0" @@ -3552,7 +3578,7 @@ conventional-changelog-angular@^5.0.12: conventional-changelog-core@^4.2.2: version "4.2.3" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.3.tgz#ce44d4bbba4032e3dc14c00fcd5b53fc00b66433" + resolved "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.3.tgz" integrity sha512-MwnZjIoMRL3jtPH5GywVNqetGILC7g6RQFvdb8LRU/fA/338JbeWAku3PZ8yQ+mtVRViiISqJlb0sOz0htBZig== dependencies: add-stream "^1.0.0" @@ -3572,12 +3598,12 @@ conventional-changelog-core@^4.2.2: conventional-changelog-preset-loader@^2.3.4: version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" + resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz" integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== conventional-changelog-writer@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz#c4042f3f1542f2f41d7d2e0d6cad23aba8df8eec" + resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz" integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== dependencies: conventional-commits-filter "^2.0.7" @@ -3592,7 +3618,7 @@ conventional-changelog-writer@^5.0.0: conventional-commits-filter@^2.0.7: version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" + resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz" integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== dependencies: lodash.ismatch "^4.4.0" @@ -3600,7 +3626,7 @@ conventional-commits-filter@^2.0.7: conventional-commits-parser@^3.2.0: version "3.2.1" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz#ba44f0b3b6588da2ee9fd8da508ebff50d116ce2" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz" integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== dependencies: JSONStream "^1.0.4" @@ -3613,7 +3639,7 @@ conventional-commits-parser@^3.2.0: conventional-recommended-bump@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" + resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz" integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== dependencies: concat-stream "^2.0.0" @@ -3627,34 +3653,34 @@ conventional-recommended-bump@^6.1.0: convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== core-js@^2.4.0: version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cosmiconfig@^5.0.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: import-fresh "^2.0.0" @@ -3664,7 +3690,7 @@ cosmiconfig@^5.0.0: cosmiconfig@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== dependencies: "@types/parse-json" "^4.0.0" @@ -3673,37 +3699,21 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" - create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-env@^5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" + resolved "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz" integrity sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ== dependencies: cross-spawn "^6.0.5" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^6.0.5: version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" @@ -3714,26 +3724,21 @@ cross-spawn@^6.0.5: cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= - css-color-names@0.0.4, css-color-names@^0.0.4: version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz" integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= css-declaration-sorter@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz" integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== dependencies: postcss "^7.0.1" @@ -3741,7 +3746,7 @@ css-declaration-sorter@^4.0.1: css-modules-loader-core@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16" + resolved "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz" integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY= dependencies: icss-replace-symbols "1.1.0" @@ -3753,12 +3758,12 @@ css-modules-loader-core@^1.1.0: css-select-base-adapter@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== css-select@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== dependencies: boolbase "^1.0.0" @@ -3768,7 +3773,7 @@ css-select@^2.0.0: css-select@^4.1.3: version "4.1.3" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + resolved "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz" integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== dependencies: boolbase "^1.0.0" @@ -3779,7 +3784,7 @@ css-select@^4.1.3: css-selector-tokenizer@^0.7.0: version "0.7.3" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz#735f26186e67c749aaf275783405cf0661fae8f1" + resolved "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz" integrity sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg== dependencies: cssesc "^3.0.0" @@ -3787,7 +3792,7 @@ css-selector-tokenizer@^0.7.0: css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== dependencies: mdn-data "2.0.4" @@ -3795,7 +3800,7 @@ css-tree@1.0.0-alpha.37: css-tree@^1.0.0-alpha.39, css-tree@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: mdn-data "2.0.14" @@ -3803,22 +3808,22 @@ css-tree@^1.0.0-alpha.39, css-tree@^1.1.2: css-what@^3.2.1: version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" + resolved "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== css-what@^5.0.0, css-what@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + resolved "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz" integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== cssesc@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== cssnano-preset-default@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" + resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz" integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ== dependencies: css-declaration-sorter "^4.0.1" @@ -3854,29 +3859,29 @@ cssnano-preset-default@^4.0.8: cssnano-util-get-arguments@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + resolved "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz" integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= cssnano-util-get-match@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + resolved "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz" integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= cssnano-util-raw-cache@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + resolved "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz" integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== dependencies: postcss "^7.0.0" cssnano-util-same-parent@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + resolved "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz" integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== cssnano@^4.1.10: version "4.1.11" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" + resolved "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz" integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g== dependencies: cosmiconfig "^5.0.0" @@ -3886,90 +3891,95 @@ cssnano@^4.1.10: csso@^4.0.2: version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + resolved "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: css-tree "^1.1.2" cssom@^0.4.4, cssom@^0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + 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.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" dargs@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-urls@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== dependencies: abab "^2.0.3" whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^2.16.1: + version "2.28.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" + integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== + dateformat@^3.0.0: version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== debug@2.6.9, debug@^2.6.9: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" debug@^3.1.0: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.3.3: +debug@^4.3.3, debug@^4.3.4: version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= decamelize-keys@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= dependencies: decamelize "^1.1.0" @@ -3977,22 +3987,22 @@ decamelize-keys@^1.1.0: decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decimal.js@^10.2.1: version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== 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" + resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-extend@^0.6.0: @@ -4002,76 +4012,76 @@ deep-extend@^0.6.0: deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deepmerge@^4.2.2: version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= depd@^1.1.2, depd@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== destroy@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== devtools-protocol@0.0.869402: version "0.0.869402" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.869402.tgz#03ade701761742e43ae4de5dc188bcd80f156d8d" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" integrity sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA== dezalgo@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz" integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= dependencies: asap "^2.0.0" @@ -4079,41 +4089,41 @@ dezalgo@^1.0.0: diff-sequences@^27.0.6: version "27.0.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== diff-sequences@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== -diff@^3.0.1, diff@^3.1.0, diff@^3.2.0: +diff@^3.1.0, diff@^3.2.0: version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dom-serializer@0: version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== dependencies: domelementtype "^2.0.1" @@ -4121,7 +4131,7 @@ dom-serializer@0: dom-serializer@^1.0.1, dom-serializer@^1.3.2: version "1.3.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz" integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: domelementtype "^2.0.1" @@ -4130,31 +4140,31 @@ dom-serializer@^1.0.1, dom-serializer@^1.3.2: domelementtype@1: version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== domexception@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== dependencies: webidl-conversions "^5.0.0" domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz" integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== dependencies: domelementtype "^2.2.0" domutils@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== dependencies: dom-serializer "0" @@ -4162,47 +4172,35 @@ domutils@^1.7.0: domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" + resolved "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz" integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== dependencies: dom-serializer "^1.0.1" domelementtype "^2.2.0" domhandler "^4.2.0" -dot-prop@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" - integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== - dependencies: - is-obj "^1.0.0" - dot-prop@^5.1.0, dot-prop@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" dot-prop@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz" integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: is-obj "^2.0.0" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - duplexer@^0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" @@ -4210,95 +4208,93 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: version "1.3.784" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.784.tgz#c370be79374b02b7f13e8a8fb0d7a02641161dac" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.784.tgz" integrity sha512-JTPxdUibkefeomWNaYs8lI/x/Zb4cOhZWX+d7kpzsNKzUd07pNuo/AcHeNJ/qgEchxM1IAxda9aaGUhKN/poOg== electron-to-chromium@^1.3.857: version "1.3.860" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.860.tgz#d612e54ed75fa524c12af8da3ad8121ebfe2802b" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.860.tgz" integrity sha512-gWwGZ+Wv4Mou2SJRH6JQzhTPjL5f95SX7n6VkLTQ/Q/INsZLZNQ1vH2GlZjozKyvT0kkFuCmWTwIoCj+/hUDPw== electron-to-chromium@^1.4.17: version "1.4.37" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.37.tgz#eedd53cad229ae2d1632b958a92a3d7d7b27f553" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.37.tgz" integrity sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg== emittery@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.12: version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 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.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.4: version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== err-code@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.17.2, es-abstract@^1.19.1: version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: call-bind "^1.0.2" @@ -4324,7 +4320,7 @@ es-abstract@^1.17.2, es-abstract@^1.19.1: es-abstract@^1.18.0-next.2: version "1.18.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz" integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== dependencies: call-bind "^1.0.2" @@ -4346,12 +4342,12 @@ es-abstract@^1.18.0-next.2: es-module-lexer@^0.9.3: version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -4360,12 +4356,12 @@ es-to-primitive@^1.2.1: es6-promise@^4.0.3: version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== es6-promisify@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= dependencies: es6-promise "^4.0.3" @@ -4387,7 +4383,7 @@ esbuild-darwin-64@0.14.38: esbuild-darwin-arm64@0.14.38: version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz#01eb6650ec010b18c990e443a6abcca1d71290a9" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz" integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== esbuild-freebsd-64@0.14.38: @@ -4472,7 +4468,7 @@ esbuild-windows-arm64@0.14.38: esbuild@^0.14.38: version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.38.tgz#99526b778cd9f35532955e26e1709a16cca2fb30" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz" integrity sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA== optionalDependencies: esbuild-android-64 "0.14.38" @@ -4498,32 +4494,32 @@ esbuild@^0.14.38: escalade@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" @@ -4533,171 +4529,161 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-google@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.11.0.tgz#fd0fc70be2e9114df097cac93a9b5b0d4e1c6830" - integrity sha512-z541Fs5TFaY7/35v/z100InQ2f3V2J7e3u/0yKrnImgsHjh6JWgSRngfC/mZepn/+XN16jUydt64k//kxXc1fw== +eslint-config-google@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" + integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== -eslint-plugin-svelte3@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-2.7.3.tgz#e793b646b848e717674fe668c21b909cfa025eb3" - integrity sha512-p6HhxyICX9x/x+8WSy6AVk2bmv9ayoznoTSyCvK47th/k/07ksuJixMwbGX9qxJVAmPBaYMjEIMSEZtJHPIN7w== +eslint-plugin-svelte3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3" + integrity sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g== -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.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== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: - eslint-visitor-keys "^1.1.0" + esrecurse "^4.3.0" + estraverse "^5.2.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-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.5.0: - version "7.31.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.31.0.tgz#f972b539424bf2604907a970860732c5d99d3aca" - integrity sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "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@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.15.0.tgz#fea1d55a7062da48d82600d2e0974c55612a11e9" + integrity sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" + "@eslint/eslintrc" "^1.2.3" + "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" 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" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.2" 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" + glob-parent "^6.0.1" globals "^13.6.0" - ignore "^4.0.6" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" 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" + regexpp "^3.2.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^6.0.9" 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== +espree@^9.3.2: + version "9.3.2" + resolved "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz" + integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.7.1" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== estree-walker@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== estree-walker@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== estree-walker@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= eventemitter3@^4.0.4: version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4712,26 +4698,26 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-brackets@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz" integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= dependencies: is-posix-bracket "^0.1.0" expand-range@^1.8.1: version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + resolved "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz" integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= dependencies: fill-range "^2.1.0" expect@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.2.4.tgz#4debf546050bcdad8914a8c95fec7662e02bf67c" + resolved "https://registry.npmjs.org/expect/-/expect-27.2.4.tgz" integrity sha512-gOtuonQ8TCnbNNCSw2fhVzRf8EFYDII4nB5NmG4IEV0rbUnW1I5zXvoTntU4iicB/Uh0oZr20NGlOLdJiwsOZA== dependencies: "@jest/types" "^27.2.4" @@ -4743,7 +4729,7 @@ expect@^27.2.4: expect@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz" integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: "@jest/types" "^27.5.1" @@ -4753,7 +4739,7 @@ expect@^27.5.1: express@^4.16.4: version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: accepts "~1.3.7" @@ -4789,12 +4775,12 @@ express@^4.16.4: extend@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -4803,14 +4789,14 @@ external-editor@^3.0.3: extglob@^0.3.1: version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + resolved "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz" integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= dependencies: is-extglob "^1.0.0" extract-zip@^1.6.6: version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz" integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== dependencies: concat-stream "^1.6.2" @@ -4820,7 +4806,7 @@ extract-zip@^1.6.6: extract-zip@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: debug "^4.1.1" @@ -4831,22 +4817,22 @@ extract-zip@^2.0.0: extsprintf@1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.1.1: version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz" integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4855,19 +4841,30 @@ fast-glob@^3.1.1: 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" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + 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-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fast-mhtml@^1.1.9: version "1.1.9" - resolved "https://registry.yarnpkg.com/fast-mhtml/-/fast-mhtml-1.1.9.tgz#2c31e0727e0eb93423938a0812b001f67a3f6575" + resolved "https://registry.npmjs.org/fast-mhtml/-/fast-mhtml-1.1.9.tgz" integrity sha512-o+oAIqvK0xlb3o0wbReLxYXdSszbJW4gBeTFAOB3N+ByxBou2cYepTtlIkzXnlQUii4HSixJFMDVYNO4ei2s/w== dependencies: bluebird "^3.5.2" @@ -4878,69 +4875,69 @@ fast-mhtml@^1.1.9: fastparse@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" + resolved "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== fastq@^1.6.0: version "1.11.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz" integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz" integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== dependencies: bser "2.1.1" fd-slicer@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= dependencies: pend "~1.2.0" fflate@^0.4.4: version "0.4.8" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz" integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== figures@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= dependencies: escape-string-regexp "^1.0.5" figures@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" filename-regex@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + resolved "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz" integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= filename-reserved-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" + resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz" integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= filenamify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" + resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz" integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== dependencies: filename-reserved-regex "^2.0.0" @@ -4949,7 +4946,7 @@ filenamify@^2.1.0: fill-range@^2.1.0: version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz" integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== dependencies: is-number "^2.1.0" @@ -4960,19 +4957,19 @@ fill-range@^2.1.0: fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" filter-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= finalhandler@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" @@ -4985,7 +4982,7 @@ finalhandler@~1.1.2: find-cache-dir@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== dependencies: commondir "^1.0.1" @@ -4994,14 +4991,14 @@ find-cache-dir@^3.3.2: find-up@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -5009,22 +5006,15 @@ find-up@^4.0.0, find-up@^4.1.0: find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" -findup-sync@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" - integrity sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY= - dependencies: - glob "~5.0.0" - flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -5032,36 +5022,36 @@ flat-cache@^3.0.4: flatted@^3.1.0: version "3.2.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz" integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg== for-each@^0.3.3: version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== dependencies: is-callable "^1.1.3" for-in@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + resolved "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz" integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= dependencies: for-in "^1.0.1" forever-agent@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -5070,7 +5060,7 @@ form-data@^3.0.0: form-data@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" @@ -5079,22 +5069,22 @@ form-data@~2.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^10.0.0: version "10.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz" integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== dependencies: graceful-fs "^4.2.0" @@ -5103,7 +5093,7 @@ fs-extra@^10.0.0: fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -5113,41 +5103,41 @@ fs-extra@^9.1.0: fs-minipass@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: minipass "^2.6.0" fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gauge@~2.7.3: version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" @@ -5161,24 +5151,24 @@ gauge@~2.7.3: generic-names@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" + resolved "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz" integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ== dependencies: loader-utils "^1.1.0" gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -5187,12 +5177,12 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-pkg-repo@^4.0.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.1.2.tgz#c4ffd60015cf091be666a0212753fc158f01a4c0" + resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.1.2.tgz" integrity sha512-/FjamZL9cBYllEbReZkxF2IMh80d8TJoC4e3bmLNif8ibHw95aj0N/tzqK0kZz9eU/3w3dL6lF4fnnX/sDdW3A== dependencies: "@hutson/parse-repository-url" "^3.0.0" @@ -5202,39 +5192,39 @@ get-pkg-repo@^4.0.0: get-port@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= get-port@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== get-stdin@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz" integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= +get-stdin@~9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" + integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" @@ -5242,14 +5232,14 @@ get-symbol-description@^1.0.0: getpass@^0.1.1: version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-raw-commits@^2.0.8: version "2.0.10" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz" integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== dependencies: dargs "^7.0.0" @@ -5260,7 +5250,7 @@ git-raw-commits@^2.0.8: git-remote-origin-url@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz" integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= dependencies: gitconfiglocal "^1.0.0" @@ -5268,7 +5258,7 @@ git-remote-origin-url@^2.0.0: git-semver-tags@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" + resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz" integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== dependencies: meow "^8.0.0" @@ -5276,7 +5266,7 @@ git-semver-tags@^4.1.1: git-up@^4.0.0: version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" + resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz" integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== dependencies: is-ssh "^1.3.0" @@ -5284,21 +5274,21 @@ git-up@^4.0.0: git-url-parse@^11.4.4: version "11.5.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.5.0.tgz#acaaf65239cb1536185b19165a24bbc754b3f764" + resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz" integrity sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA== dependencies: git-up "^4.0.0" gitconfiglocal@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz" integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= dependencies: ini "^1.3.2" glob-base@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + resolved "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz" integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= dependencies: glob-parent "^2.0.0" @@ -5306,21 +5296,28 @@ glob-base@^0.3.0: glob-parent@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz" integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= dependencies: is-glob "^2.0.0" glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -5330,39 +5327,33 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~5.0.0: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= +glob@~7.2.0: + version "7.2.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.2.tgz#29deb38e1ef90f132d5958abe9c3ee8e87f3c318" + integrity sha512-NzDgHDiJwKYByLrL5lONmQFpK/2G78SMMfo+E9CuGlX4IkvfKDsiQSNPwAYxEy+e6p7ZQ3uslSLlwlJcqezBmQ== dependencies: + fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "2 || 3" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - dependencies: - ini "^1.3.4" - globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" - integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== + version "13.15.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz" + integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== dependencies: type-fest "^0.20.2" -globby@^11.0.2: +globby@^11.0.2, globby@^11.0.4: version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + resolved "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== dependencies: array-union "^2.1.0" @@ -5372,9 +5363,21 @@ globby@^11.0.2: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + resolved "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz" integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= dependencies: array-union "^1.0.1" @@ -5385,44 +5388,27 @@ globby@^6.1.0: glur@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/glur/-/glur-1.1.2.tgz#f20ea36db103bfc292343921f1f91e83c3467689" + resolved "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz" integrity sha1-8g6jbbEDv8KSNDkh8fkeg8NGdok= -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== graceful-fs@^4.2.9: version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== handlebars@^4.7.6: version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" @@ -5434,12 +5420,12 @@ handlebars@^4.7.6: har-schema@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.3: version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" @@ -5447,107 +5433,107 @@ har-validator@~5.1.3: hard-rejection@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== harmony-reflect@^1.4.6: version "1.6.2" - resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" + resolved "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz" integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g== has-ansi@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has@^1.0.0, has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hex-color-regex@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz" integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== dependencies: lru-cache "^6.0.0" hsl-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz" integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= hsla-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= html-encoding-sniffer@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== dependencies: whatwg-encoding "^1.0.5" html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== htmlparser2@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== dependencies: domelementtype "^2.0.1" @@ -5557,12 +5543,12 @@ htmlparser2@^6.1.0: http-cache-semantics@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.7.2: version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" @@ -5573,7 +5559,7 @@ http-errors@1.7.2: http-errors@~1.7.2: version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" @@ -5584,7 +5570,7 @@ http-errors@~1.7.2: http-proxy-agent@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== dependencies: "@tootallnate/once" "1" @@ -5593,7 +5579,7 @@ http-proxy-agent@^4.0.1: http-signature@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" @@ -5602,7 +5588,7 @@ http-signature@~1.2.0: https-proxy-agent@^2.2.1: version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== dependencies: agent-base "^4.3.0" @@ -5610,7 +5596,7 @@ https-proxy-agent@^2.2.1: https-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== dependencies: agent-base "6" @@ -5618,86 +5604,86 @@ https-proxy-agent@^5.0.0: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-ms@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= dependencies: ms "^2.0.0" iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.6.2: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + resolved "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= identity-obj-proxy@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= dependencies: harmony-reflect "^1.4.6" ieee754@^1.1.13: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-styles@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ignore-styles/-/ignore-styles-5.0.1.tgz#b49ef2274bdafcd8a4880a966bfe38d1a0bf4671" + resolved "https://registry.npmjs.org/ignore-styles/-/ignore-styles-5.0.1.tgz" integrity sha1-tJ7yJ0va/NikiAqWa/440aC/RnE= ignore-walk@^3.0.3: version "3.0.4" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz" integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== 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.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.1.8, ignore@^5.2.0, ignore@~5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + import-cwd@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz" integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= dependencies: import-from "^2.1.0" import-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz" integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== dependencies: import-from "^3.0.0" import-fresh@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= dependencies: caller-path "^2.0.0" @@ -5705,7 +5691,7 @@ import-fresh@^2.0.0: import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -5713,26 +5699,21 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: import-from@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + resolved "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz" integrity sha1-M1238qev/VOqpHHUuAId7ja387E= dependencies: resolve-from "^3.0.0" import-from@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + resolved "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz" integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== dependencies: resolve-from "^5.0.0" -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - import-local@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz" integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: pkg-dir "^4.2.0" @@ -5740,27 +5721,27 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== indexes-of@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= infer-owner@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -5768,22 +5749,27 @@ inflight@^1.0.4: inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + init-package-json@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" + resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.3.tgz" integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== dependencies: glob "^7.1.1" @@ -5797,7 +5783,7 @@ init-package-json@^2.0.2: inquirer@^6.2.1: version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz" integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== dependencies: ansi-escapes "^3.2.0" @@ -5816,7 +5802,7 @@ inquirer@^6.2.1: inquirer@^7.3.3: version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" @@ -5835,7 +5821,7 @@ inquirer@^7.3.3: internal-slot@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" @@ -5844,87 +5830,80 @@ internal-slot@^1.0.3: ip@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-absolute-url@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-arrayish@^0.3.1: version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-bigint@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz" integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz" integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== dependencies: call-bind "^1.0.2" is-buffer@^1.1.5: version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== is-callable@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-ci@^1.0.10: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== - dependencies: - ci-info "^1.5.0" - is-ci@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== dependencies: ci-info "^2.0.0" is-ci@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz" integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== dependencies: ci-info "^3.1.1" is-color-stop@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= dependencies: css-color-names "^0.0.4" @@ -5936,210 +5915,180 @@ is-color-stop@^1.0.0: is-core-module@^2.2.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz" integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== dependencies: has "^1.0.3" is-core-module@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" is-date-object@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz" integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== is-directory@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-dotfile@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + resolved "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz" integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= is-equal-shallow@^0.1.3: version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + resolved "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= dependencies: is-primitive "^2.0.0" is-extendable@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extglob@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz" integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= dependencies: is-extglob "^1.0.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - is-lambda@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= is-module@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= is-negative-zero@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= - is-number-object@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz" integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== is-number@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + resolved "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz" integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= dependencies: kind-of "^3.0.2" is-number@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + resolved "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz" integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-obj@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-posix-bracket@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + resolved "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-primitive@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + resolved "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= - is-reference@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== dependencies: "@types/estree" "*" is-regex@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz" integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== dependencies: call-bind "^1.0.2" @@ -6147,7 +6096,7 @@ is-regex@^1.1.3: is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" @@ -6155,114 +6104,104 @@ is-regex@^1.1.4: is-resolvable@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-retry-allowed@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-shared-array-buffer@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== is-ssh@^1.3.0: version "1.3.3" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e" + resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.3.tgz" integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ== dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz" integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== is-string@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: text-extensions "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-weakref@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz" integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== dependencies: call-bind "^1.0.0" isarray@1.0.0, isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul-lib-coverage@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz#e8900b3ed6069759229cf30f7067388d148aeb5e" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.1.tgz" integrity sha512-GvCYYTxaCPqwMjobtVcVKvSHtAGe48MNhGjpK8LtVF8K0ISX7hCKl85LgtuaSneWVyQmaGcW3iXVV3GaZSLpmQ== istanbul-lib-coverage@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== dependencies: "@babel/core" "^7.7.5" @@ -6272,7 +6211,7 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: "@babel/core" "^7.12.3" @@ -6283,7 +6222,7 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-report@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -6292,7 +6231,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz" integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" @@ -6301,7 +6240,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz" integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: html-escaper "^2.0.0" @@ -6309,7 +6248,7 @@ istanbul-reports@^3.0.2: istanbul-reports@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz" integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== dependencies: html-escaper "^2.0.0" @@ -6317,7 +6256,7 @@ istanbul-reports@^3.1.3: jest-changed-files@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.2.4.tgz#d7de46e90e5a599c47e260760f5ab53516e835e6" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.2.4.tgz" integrity sha512-eeO1C1u4ex7pdTroYXezr+rbr957myyVoKGjcY4R1TJi3A+9v+4fu1Iv9J4eLq1bgFyT3O3iRWU9lZsEE7J72Q== dependencies: "@jest/types" "^27.2.4" @@ -6326,7 +6265,7 @@ jest-changed-files@^27.2.4: jest-changed-files@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz" integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: "@jest/types" "^27.5.1" @@ -6335,7 +6274,7 @@ jest-changed-files@^27.5.1: jest-circus@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.2.4.tgz#3bd898a29dcaf6a506f3f1b780dff5f67ca83c23" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.2.4.tgz" integrity sha512-TtheheTElrGjlsY9VxkzUU1qwIx05ItIusMVKnvNkMt4o/PeegLRcjq3Db2Jz0GGdBalJdbzLZBgeulZAJxJWA== dependencies: "@jest/environment" "^27.2.4" @@ -6360,7 +6299,7 @@ jest-circus@^27.2.4: jest-circus@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz" integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: "@jest/environment" "^27.5.1" @@ -6385,7 +6324,7 @@ jest-circus@^27.5.1: jest-cli@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.2.4.tgz#acda7f367aa6e674723fc1a7334e0ae1799448d2" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.2.4.tgz" integrity sha512-4kpQQkg74HYLaXo3nzwtg4PYxSLgL7puz1LXHj5Tu85KmlIpxQFjRkXlx4V47CYFFIDoyl3rHA/cXOxUWyMpNg== dependencies: "@jest/core" "^27.2.4" @@ -6403,7 +6342,7 @@ jest-cli@^27.2.4: jest-cli@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz" integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== dependencies: "@jest/core" "^27.5.1" @@ -6421,7 +6360,7 @@ jest-cli@^27.5.1: jest-config@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.2.4.tgz#0204969f5ae2e5190d47be2c14c04d631b7836e2" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.2.4.tgz" integrity sha512-tWy0UxhdzqiKyp4l5Vq4HxLyD+gH5td+GCF3c22/DJ0bYAOsMo+qi2XtbJI6oYMH5JOJQs9nLW/r34nvFCehjA== dependencies: "@babel/core" "^7.1.0" @@ -6448,7 +6387,7 @@ jest-config@^27.2.4: jest-config@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz" integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: "@babel/core" "^7.8.0" @@ -6478,7 +6417,7 @@ jest-config@^27.5.1: jest-diff@^23.6.0: version "23.6.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz" integrity sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g== dependencies: chalk "^2.0.1" @@ -6488,7 +6427,7 @@ jest-diff@^23.6.0: jest-diff@^27.0.0, jest-diff@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.2.4.tgz#171c51d3d2c105c457100fee6e7bf7cee51c8d8c" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.2.4.tgz" integrity sha512-bLAVlDSCR3gqUPGv+4nzVpEXGsHh98HjUL7Vb2hVyyuBDoQmja8eJb0imUABsuxBeUVmf47taJSAd9nDrwWKEg== dependencies: chalk "^4.0.0" @@ -6498,7 +6437,7 @@ jest-diff@^27.0.0, jest-diff@^27.2.4: jest-diff@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: chalk "^4.0.0" @@ -6508,21 +6447,21 @@ jest-diff@^27.5.1: jest-docblock@^27.0.6: version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz" integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA== dependencies: detect-newline "^3.0.0" jest-docblock@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" jest-each@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.2.4.tgz#b4f280aafd63129ba82e345f0e74c5a10200aeef" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.2.4.tgz" integrity sha512-w9XVc+0EDBUTJS4xBNJ7N2JCcWItFd006lFjz77OarAQcQ10eFDBMrfDv2GBJMKlXe9aq0HrIIF51AXcZrRJyg== dependencies: "@jest/types" "^27.2.4" @@ -6533,7 +6472,7 @@ jest-each@^27.2.4: jest-each@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz" integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: "@jest/types" "^27.5.1" @@ -6544,7 +6483,7 @@ jest-each@^27.5.1: jest-environment-jsdom@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.2.4.tgz#39ae80bbb8675306bfaf0440be1e5f877554539a" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.2.4.tgz" integrity sha512-X70pTXFSypD7AIzKT1mLnDi5hP9w9mdTRcOGOmoDoBrNyNEg4rYm6d4LQWFLc9ps1VnMuDOkFSG0wjSNYGjkng== dependencies: "@jest/environment" "^27.2.4" @@ -6557,7 +6496,7 @@ jest-environment-jsdom@^27.2.4: jest-environment-jsdom@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz" integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== dependencies: "@jest/environment" "^27.5.1" @@ -6570,7 +6509,7 @@ jest-environment-jsdom@^27.5.1: jest-environment-node@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.2.4.tgz#b79f98cb36e0c9111aac859c9c99f04eb2f74ff6" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.2.4.tgz" integrity sha512-ZbVbFSnbzTvhLOIkqh5lcLuGCCFvtG4xTXIRPK99rV2KzQT3kNg16KZwfTnLNlIiWCE8do960eToeDfcqmpSAw== dependencies: "@jest/environment" "^27.2.4" @@ -6582,7 +6521,7 @@ jest-environment-node@^27.2.4: jest-environment-node@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz" integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: "@jest/environment" "^27.5.1" @@ -6594,22 +6533,22 @@ jest-environment-node@^27.5.1: jest-get-type@^22.1.0: version "22.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz" integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== jest-get-type@^27.0.6: version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.0.6.tgz#0eb5c7f755854279ce9b68a9f1a4122f69047cfe" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== jest-get-type@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== jest-haste-map@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.2.4.tgz#f8974807bedf07348ca9fd24e5861ab7c8e61aba" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.2.4.tgz" integrity sha512-bkJ4bT00T2K+1NZXbRcyKnbJ42I6QBvoDNMTAQQDBhaGNnZreiQKUNqax0e6hLTx7E75pKDeltVu3V1HAdu+YA== dependencies: "@jest/types" "^27.2.4" @@ -6629,7 +6568,7 @@ jest-haste-map@^27.2.4: jest-haste-map@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: "@jest/types" "^27.5.1" @@ -6649,7 +6588,7 @@ jest-haste-map@^27.5.1: jest-image-snapshot@^4.5.1: version "4.5.1" - resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-4.5.1.tgz#79fe0419c7729eb1be6c873365307a7b60f5cda0" + resolved "https://registry.npmjs.org/jest-image-snapshot/-/jest-image-snapshot-4.5.1.tgz" integrity sha512-0YkgupgkkCx0wIZkxvqs/oNiUT0X0d2WTpUhaAp+Dy6CpqBUZMRTIZo4KR1f+dqmx6WXrLCvecjnHLIsLkI+gQ== dependencies: chalk "^1.1.3" @@ -6664,7 +6603,7 @@ jest-image-snapshot@^4.5.1: jest-jasmine2@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.2.4.tgz#4a1608133dbdb4d68b5929bfd785503ed9c9ba51" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.2.4.tgz" integrity sha512-fcffjO/xLWLVnW2ct3No4EksxM5RyPwHDYu9QU+90cC+/eSMLkFAxS55vkqsxexOO5zSsZ3foVpMQcg/amSeIQ== dependencies: "@babel/traverse" "^7.1.0" @@ -6688,7 +6627,7 @@ jest-jasmine2@^27.2.4: jest-jasmine2@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz" integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== dependencies: "@jest/environment" "^27.5.1" @@ -6711,7 +6650,7 @@ jest-jasmine2@^27.5.1: jest-leak-detector@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.2.4.tgz#9bb7eab26a73bb280e9298be8d80f389288ec8f1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.2.4.tgz" integrity sha512-SrcHWbe0EHg/bw2uBjVoHacTo5xosl068x2Q0aWsjr2yYuW2XwqrSkZV4lurUop0jhv1709ymG4or+8E4sH27Q== dependencies: jest-get-type "^27.0.6" @@ -6719,7 +6658,7 @@ jest-leak-detector@^27.2.4: jest-leak-detector@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz" integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: jest-get-type "^27.5.1" @@ -6727,7 +6666,7 @@ jest-leak-detector@^27.5.1: jest-matcher-utils@^23.6.0: version "23.6.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz" integrity sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog== dependencies: chalk "^2.0.1" @@ -6736,7 +6675,7 @@ jest-matcher-utils@^23.6.0: jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz" integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== dependencies: chalk "^4.0.0" @@ -6746,7 +6685,7 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-matcher-utils@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.2.4.tgz#008fff018151415ad1b6cfc083fd70fe1e012525" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.2.4.tgz" integrity sha512-nQeLfFAIPPkyhkDfifAPfP/U5wm1x0fLtAzqXZSSKckXDNuk2aaOfQiDYv1Mgf5GY6yOsxfUnvNm3dDjXM+BXw== dependencies: chalk "^4.0.0" @@ -6756,7 +6695,7 @@ jest-matcher-utils@^27.2.4: jest-message-util@^23.4.0: version "23.4.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz" integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8= dependencies: "@babel/code-frame" "^7.0.0-beta.35" @@ -6767,7 +6706,7 @@ jest-message-util@^23.4.0: jest-message-util@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.2.4.tgz#667e8c0f2b973156d1bac7398a7f677705cafaca" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.2.4.tgz" integrity sha512-wbKT/BNGnBVB9nzi+IoaLkXt6fbSvqUxx+IYY66YFh96J3goY33BAaNG3uPqaw/Sh/FR9YpXGVDfd5DJdbh4nA== dependencies: "@babel/code-frame" "^7.12.13" @@ -6782,7 +6721,7 @@ jest-message-util@^27.2.4: jest-message-util@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== dependencies: "@babel/code-frame" "^7.12.13" @@ -6797,7 +6736,7 @@ jest-message-util@^27.5.1: jest-mock@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.2.4.tgz#c8f0ef33f73d8ff53e3f60b16d59f1128f4072ae" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.2.4.tgz" integrity sha512-iVRU905rutaAoUcrt5Tm1JoHHWi24YabqEGXjPJI4tAyA6wZ7mzDi3GrZ+M7ebgWBqUkZE93GAx1STk7yCMIQA== dependencies: "@jest/types" "^27.2.4" @@ -6805,7 +6744,7 @@ jest-mock@^27.2.4: jest-mock@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== dependencies: "@jest/types" "^27.5.1" @@ -6813,22 +6752,22 @@ jest-mock@^27.5.1: jest-pnp-resolver@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^27.0.6: version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.0.6.tgz" integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== jest-regex-util@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== jest-resolve-dependencies@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.4.tgz#20c41cc02b66aa45169b282356ec73b133013089" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.4.tgz" integrity sha512-i5s7Uh9B3Q6uwxLpMhNKlgBf6pcemvWaORxsW1zNF/YCY3jd5EftvnGBI+fxVwJ1CBxkVfxqCvm1lpZkbaoGmg== dependencies: "@jest/types" "^27.2.4" @@ -6837,7 +6776,7 @@ jest-resolve-dependencies@^27.2.4: jest-resolve-dependencies@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz" integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: "@jest/types" "^27.5.1" @@ -6846,7 +6785,7 @@ jest-resolve-dependencies@^27.5.1: jest-resolve@^23.6.0: version "23.6.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz" integrity sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA== dependencies: browser-resolve "^1.11.3" @@ -6855,7 +6794,7 @@ jest-resolve@^23.6.0: jest-resolve@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.2.4.tgz#d3b999f073ff84a8ae109ce99ff7f3223048701a" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.2.4.tgz" integrity sha512-IsAO/3+3BZnKjI2I4f3835TBK/90dxR7Otgufn3mnrDFTByOSXclDi3G2XJsawGV4/18IMLARJ+V7Wm7t+J89Q== dependencies: "@jest/types" "^27.2.4" @@ -6871,7 +6810,7 @@ jest-resolve@^27.2.4: jest-resolve@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: "@jest/types" "^27.5.1" @@ -6887,7 +6826,7 @@ jest-resolve@^27.5.1: jest-runner@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.2.4.tgz#d816f4cb4af04f3cba703afcf5a35a335b77cad4" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.2.4.tgz" integrity sha512-hIo5PPuNUyVDidZS8EetntuuJbQ+4IHWxmHgYZz9FIDbG2wcZjrP6b52uMDjAEQiHAn8yn8ynNe+TL8UuGFYKg== dependencies: "@jest/console" "^27.2.4" @@ -6915,7 +6854,7 @@ jest-runner@^27.2.4: jest-runner@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz" integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: "@jest/console" "^27.5.1" @@ -6942,7 +6881,7 @@ jest-runner@^27.5.1: jest-runtime@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.2.4.tgz#170044041e5d30625ab8d753516bbe503f213a5c" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.2.4.tgz" integrity sha512-ICKzzYdjIi70P17MZsLLIgIQFCQmIjMFf+xYww3aUySiUA/QBPUTdUqo5B2eg4HOn9/KkUsV0z6GVgaqAPBJvg== dependencies: "@jest/console" "^27.2.4" @@ -6975,7 +6914,7 @@ jest-runtime@^27.2.4: jest-runtime@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz" integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== dependencies: "@jest/environment" "^27.5.1" @@ -7003,7 +6942,7 @@ jest-runtime@^27.5.1: jest-serializer@^27.0.6: version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz" integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA== dependencies: "@types/node" "*" @@ -7011,7 +6950,7 @@ jest-serializer@^27.0.6: jest-serializer@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz" integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: "@types/node" "*" @@ -7019,7 +6958,7 @@ jest-serializer@^27.5.1: jest-snapshot@^23.6.0: version "23.6.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-23.6.0.tgz" integrity sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg== dependencies: babel-types "^6.0.0" @@ -7035,7 +6974,7 @@ jest-snapshot@^23.6.0: jest-snapshot@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.2.4.tgz#277b2269437e3ffcb91d95a73b24becf33c5a871" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.2.4.tgz" integrity sha512-5DFxK31rYS8X8C6WXsFx8XxrxW3PGa6+9IrUcZdTLg1aEyXDGIeiBh4jbwvh655bg/9vTETbEj/njfZicHTZZw== dependencies: "@babel/core" "^7.7.2" @@ -7065,7 +7004,7 @@ jest-snapshot@^27.2.4: jest-snapshot@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz" integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: "@babel/core" "^7.7.2" @@ -7093,7 +7032,7 @@ jest-snapshot@^27.5.1: jest-util@^27.0.0, jest-util@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.2.4.tgz#3d7ce081b2e7f4cfe0156452ac01f3cb456cc656" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.2.4.tgz" integrity sha512-mW++4u+fSvAt3YBWm5IpbmRAceUqa2B++JlUZTiuEt2AmNYn0Yw5oay4cP17TGsMINRNPSGiJ2zNnX60g+VbFg== dependencies: "@jest/types" "^27.2.4" @@ -7105,7 +7044,7 @@ jest-util@^27.0.0, jest-util@^27.2.4: jest-util@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz" integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: "@jest/types" "^27.5.1" @@ -7117,7 +7056,7 @@ jest-util@^27.5.1: jest-validate@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.2.4.tgz#b66d462b2fb93d7e16a47d1aa8763d5600bf2cfa" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.2.4.tgz" integrity sha512-VMtbxbkd7LHnIH7PChdDtrluCFRJ4b1YV2YJzNwwsASMWftq/HgqiqjvptBOWyWOtevgO3f14wPxkPcLlVBRog== dependencies: "@jest/types" "^27.2.4" @@ -7129,7 +7068,7 @@ jest-validate@^27.2.4: jest-validate@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: "@jest/types" "^27.5.1" @@ -7141,7 +7080,7 @@ jest-validate@^27.5.1: jest-watcher@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.2.4.tgz#b1d5c39ab94f59f4f35f66cc96f7761a10e0cfc4" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.2.4.tgz" integrity sha512-LXC/0+dKxhK7cfF7reflRYlzDIaQE+fL4ynhKhzg8IMILNMuI4xcjXXfUJady7OR4/TZeMg7X8eHx8uan9vqaQ== dependencies: "@jest/test-result" "^27.2.4" @@ -7154,7 +7093,7 @@ jest-watcher@^27.2.4: jest-watcher@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz" integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: "@jest/test-result" "^27.5.1" @@ -7167,7 +7106,7 @@ jest-watcher@^27.5.1: jest-worker@^26.2.1: version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" @@ -7176,7 +7115,7 @@ jest-worker@^26.2.1: jest-worker@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.2.4.tgz#881455df75e22e7726a53f43703ab74d6b36f82d" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.4.tgz" integrity sha512-Zq9A2Pw59KkVjBBKD1i3iE2e22oSjXhUKKuAK1HGX8flGwkm6NMozyEYzKd41hXc64dbd/0eWFeEEuxqXyhM+g== dependencies: "@types/node" "*" @@ -7185,7 +7124,7 @@ jest-worker@^27.2.4: jest-worker@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" @@ -7194,7 +7133,7 @@ jest-worker@^27.5.1: jest@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.2.4.tgz#70e27bef873138afc123aa4769f7124c50ad3efb" + resolved "https://registry.npmjs.org/jest/-/jest-27.2.4.tgz" integrity sha512-h4uqb1EQLfPulWyUFFWv9e9Nn8sCqsJ/j3wk/KCY0p4s4s0ICCfP3iMf6hRf5hEhsDyvyrCgKiZXma63gMz16A== dependencies: "@jest/core" "^27.2.4" @@ -7203,7 +7142,7 @@ jest@^27.2.4: jest@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: "@jest/core" "^27.5.1" @@ -7212,35 +7151,37 @@ jest@^27.5.1: joycon@^3.0.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdom@^16.4.0: version "16.6.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.6.0.tgz#f79b3786682065492a3da6a60a4695da983805ac" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz" integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== dependencies: abab "^2.0.5" @@ -7273,7 +7214,7 @@ jsdom@^16.4.0: jsdom@^16.6.0: version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: abab "^2.0.5" @@ -7306,66 +7247,61 @@ jsdom@^16.6.0: jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + 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.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@2.x, json5@^2.1.2: version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" json5@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" -jsonc-parser@^3.0.0: +jsonc-parser@^3.0.0, jsonc-parser@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz" integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -7374,12 +7310,12 @@ jsonfile@^6.0.1: jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsprim@^1.2.2: version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" @@ -7389,31 +7325,24 @@ jsprim@^1.2.2: kind-of@^3.0.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@^3.0.0, kleur@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= - dependencies: - package-json "^4.0.0" - lerna@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" + resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz" integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== dependencies: "@lerna/add" "4.0.0" @@ -7437,12 +7366,12 @@ lerna@^4.0.0: leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -7450,7 +7379,7 @@ levn@^0.4.1: levn@~0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" @@ -7458,7 +7387,7 @@ levn@~0.3.0: libnpmaccess@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-4.0.3.tgz#dfb0e5b0a53c315a2610d300e46b4ddeb66e7eec" + resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-4.0.3.tgz" integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== dependencies: aproba "^2.0.0" @@ -7468,7 +7397,7 @@ libnpmaccess@^4.0.1: libnpmpublish@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-4.0.2.tgz#be77e8bf5956131bcb45e3caa6b96a842dec0794" + resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-4.0.2.tgz" integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== dependencies: normalize-package-data "^3.0.2" @@ -7479,17 +7408,24 @@ libnpmpublish@^4.0.0: lines-and-columns@^1.1.6: version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkify-it@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" + integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== + dependencies: + uc.micro "^1.0.1" + livereload-js@^3.3.1: version "3.3.2" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.3.2.tgz#c88b009c6e466b15b91faa26fd7c99d620e12651" + resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.2.tgz" integrity sha512-w677WnINxFkuixAoUEXOStewzLYGI76XVag+0JWMMEyjJQKs0ibWZMxkTlB96Lm3EjZ7IeOxVziBEbtxVQqQZA== livereload@^0.9.1: version "0.9.3" - resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.3.tgz#a714816375ed52471408bede8b49b2ee6a0c55b1" + resolved "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz" integrity sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw== dependencies: chokidar "^3.5.0" @@ -7499,7 +7435,7 @@ livereload@^0.9.1: load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" @@ -7509,7 +7445,7 @@ load-json-file@^4.0.0: load-json-file@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz" integrity sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ== dependencies: graceful-fs "^4.1.15" @@ -7519,7 +7455,7 @@ load-json-file@^6.2.0: loader-utils@^1.1.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== dependencies: big.js "^5.2.2" @@ -7528,12 +7464,12 @@ loader-utils@^1.1.0: local-access@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/local-access/-/local-access-1.1.0.tgz#e007c76ba2ca83d5877ba1a125fc8dfe23ba4798" + resolved "https://registry.npmjs.org/local-access/-/local-access-1.1.0.tgz" integrity sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw== locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" @@ -7541,51 +7477,46 @@ locate-path@^2.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash._reinterpolate@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + 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 sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.ismatch@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.template@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz" integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== dependencies: lodash._reinterpolate "^3.0.0" @@ -7593,63 +7524,38 @@ lodash.template@^4.5.0: lodash.templatesettings@^4.0.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz" integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== 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 sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - lodash.uniq@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.x, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.7.0: +lodash@4.x, lodash@^4.17.12, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" magic-string@^0.25.7: version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== dependencies: sourcemap-codec "^1.4.4" -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -7657,19 +7563,19 @@ make-dir@^2.1.0: make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" make-error@1.x, make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== make-fetch-happen@^8.0.9: version "8.0.14" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz" integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== dependencies: agentkeepalive "^4.1.3" @@ -7690,7 +7596,7 @@ make-fetch-happen@^8.0.9: make-fetch-happen@^9.0.1: version "9.0.4" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz#ceaa100e60e0ef9e8d1ede94614bb2ba83c8bb24" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz" integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== dependencies: agentkeepalive "^4.1.3" @@ -7712,44 +7618,88 @@ make-fetch-happen@^9.0.1: makeerror@1.0.x: version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= dependencies: tmpl "1.0.x" map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^4.0.0: version "4.2.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz" integrity sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ== +markdown-it@12.3.2: + version "12.3.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== + dependencies: + argparse "^2.0.1" + entities "~2.1.0" + linkify-it "^3.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdownlint-cli@^0.31.1: + version "0.31.1" + resolved "https://registry.yarnpkg.com/markdownlint-cli/-/markdownlint-cli-0.31.1.tgz#8db34eec453e84bed06a954c8a289333f7c2c1c7" + integrity sha512-keIOMwQn+Ch7MoBwA+TdkyVMuxAeZFEGmIIlvwgV0Z1TGS5MxPnRr29XCLhkNzCHU+uNKGjU+VEjLX+Z9kli6g== + dependencies: + commander "~9.0.0" + get-stdin "~9.0.0" + glob "~7.2.0" + ignore "~5.2.0" + js-yaml "^4.1.0" + jsonc-parser "~3.0.0" + markdownlint "~0.25.1" + markdownlint-rule-helpers "~0.16.0" + minimatch "~3.0.5" + run-con "~1.2.10" + +markdownlint-rule-helpers@~0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.16.0.tgz#c327f72782bd2b9475127a240508231f0413a25e" + integrity sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w== + +markdownlint@^0.25.1, markdownlint@~0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.25.1.tgz#df04536607ebeeda5ccd5e4f38138823ed623788" + integrity sha512-AG7UkLzNa1fxiOv5B+owPsPhtM4D6DoODhsJgiaNg1xowXovrYgOnLqAgOOFQpWOlHFVQUzjMY5ypNNTeov92g== + dependencies: + markdown-it "12.3.2" + math-random@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== mdn-data@2.0.14: version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== mdn-data@2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + media-typer@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= meow@^7.0.0: version "7.1.1" - resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" + resolved "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz" integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== dependencies: "@types/minimist" "^1.2.0" @@ -7766,7 +7716,7 @@ meow@^7.0.0: meow@^8.0.0: version "8.1.2" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== dependencies: "@types/minimist" "^1.2.0" @@ -7783,27 +7733,27 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^2.3.11: version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz" integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= dependencies: arr-diff "^2.0.0" @@ -7822,7 +7772,7 @@ micromatch@^2.3.11: micromatch@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" @@ -7830,63 +7780,70 @@ micromatch@^4.0.4: mime-db@1.48.0: version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz" integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== mime-db@1.49.0: version "1.49.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-types@^2.1.12, mime-types@~2.1.24: version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz" integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== dependencies: mime-db "1.48.0" mime-types@~2.1.19: version "2.1.32" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: mime-db "1.49.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.0.3, mime@^2.3.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + resolved "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== mimic-fn@^1.0.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== min-indent@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -"minimatch@2 || 3", minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@~3.0.5: + version "3.0.8" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" minimist-options@4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: arrify "^1.0.1" @@ -7895,24 +7852,19 @@ minimist-options@4.1.0: minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= - minipass-collect@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: version "1.3.4" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.4.tgz#63f5af868a38746ca7b33b03393ddf8c291244fe" + resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz" integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ== dependencies: minipass "^3.1.0" @@ -7923,14 +7875,14 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: minipass-flush@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-json-stream@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz" integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== dependencies: jsonparse "^1.3.1" @@ -7938,21 +7890,21 @@ minipass-json-stream@^1.0.1: minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass-sized@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== dependencies: minipass "^3.0.0" minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" @@ -7960,21 +7912,21 @@ minipass@^2.6.0, minipass@^2.9.0: minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz" integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== dependencies: yallist "^4.0.0" minizlib@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: minipass "^2.9.0" minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -7982,17 +7934,17 @@ minizlib@^2.0.0, minizlib@^2.1.1: mitt@^1.1.3: version "1.2.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" + resolved "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz" integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== mkdirp-classic@^0.5.2: version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== mkdirp-infer-owner@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" + resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz" integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== dependencies: chownr "^2.0.0" @@ -8001,49 +7953,49 @@ mkdirp-infer-owner@^2.0.0: mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== modify-values@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== mri@^1.1.0: version "1.1.6" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.6.tgz#49952e1044db21dbf90f6cd92bc9c9a777d415a6" + resolved "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz" integrity sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ== ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@^2.0.0, ms@^2.1.1: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multimatch@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== dependencies: "@types/minimatch" "^3.0.3" @@ -8054,44 +8006,44 @@ multimatch@^5.0.0: mute-stream@0.0.7: version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= negotiator@0.6.2, negotiator@^0.6.2: version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== neo-async@^2.6.0: version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nice-try@^1.0.4: version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-fetch@^2.6.1: version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" node-gyp@^5.0.2: version "5.1.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz" integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== dependencies: env-paths "^2.2.0" @@ -8108,7 +8060,7 @@ node-gyp@^5.0.2: node-gyp@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz" integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== dependencies: env-paths "^2.2.0" @@ -8124,32 +8076,32 @@ node-gyp@^7.1.0: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= node-modules-regexp@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-releases@^1.1.71: version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz" integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== node-releases@^1.1.77: version "1.1.77" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz" integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== node-releases@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== nopt@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -8157,14 +8109,14 @@ nopt@^4.0.1: nopt@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== dependencies: abbrev "1" normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -8174,7 +8126,7 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.2.tgz#cae5c410ae2434f9a6c1baa65d5bc3b9366c8699" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz" integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== dependencies: hosted-git-info "^4.0.1" @@ -8184,43 +8136,43 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: normalize-path@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^3.0.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== normalize-url@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-bundled@^1.1.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" + resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" npm-install-checks@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" + resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz" integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== dependencies: semver "^7.1.1" npm-lifecycle@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" + resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz" integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== dependencies: byline "^5.0.0" @@ -8234,12 +8186,12 @@ npm-lifecycle@^3.1.5: npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: version "8.1.5" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" + resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz" integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== dependencies: hosted-git-info "^4.0.1" @@ -8248,7 +8200,7 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack npm-packlist@^2.1.4: version "2.2.2" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz" integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== dependencies: glob "^7.1.6" @@ -8258,7 +8210,7 @@ npm-packlist@^2.1.4: npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" + resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz" integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== dependencies: npm-install-checks "^4.0.0" @@ -8268,7 +8220,7 @@ npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: npm-registry-fetch@^11.0.0: version "11.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz" integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== dependencies: make-fetch-happen "^9.0.1" @@ -8280,7 +8232,7 @@ npm-registry-fetch@^11.0.0: npm-registry-fetch@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz" integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== dependencies: "@npmcli/ci-detect" "^1.0.0" @@ -8292,23 +8244,16 @@ npm-registry-fetch@^9.0.0: minizlib "^2.0.0" npm-package-arg "^8.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npmlog@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" @@ -8318,51 +8263,51 @@ npmlog@^4.1.2: nth-check@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" nth-check@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz" integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== dependencies: boolbase "^1.0.0" number-is-nan@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz" integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" @@ -8372,7 +8317,7 @@ object.assign@^4.1.2: object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz" integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== dependencies: call-bind "^1.0.2" @@ -8381,7 +8326,7 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1 object.getownpropertydescriptors@^2.1.0: version "2.1.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz" integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: call-bind "^1.0.2" @@ -8390,7 +8335,7 @@ object.getownpropertydescriptors@^2.1.0: object.omit@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + resolved "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz" integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= dependencies: for-own "^0.1.4" @@ -8398,7 +8343,7 @@ object.omit@^2.0.0: object.values@^1.1.0: version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: call-bind "^1.0.2" @@ -8407,43 +8352,35 @@ object.values@^1.1.0: on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -optimist@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - optionator@^0.8.1: version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" @@ -8455,7 +8392,7 @@ optionator@^0.8.1: optionator@^0.9.1: version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -8467,22 +8404,22 @@ optionator@^0.9.1: "opts@>= 1.2.0": version "2.0.2" - resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1" + resolved "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz" integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg== os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -8490,71 +8427,71 @@ osenv@^0.1.4: p-finally@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-map-series@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" + resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== p-map@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-pipe@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-3.1.0.tgz#48b57c922aa2e1af6a6404cb7c6bf0eb9cc8e60e" + resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz" integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== p-queue@^6.3.0, p-queue@^6.6.2: version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== dependencies: eventemitter3 "^4.0.4" @@ -8562,46 +8499,36 @@ p-queue@^6.3.0, p-queue@^6.6.2: p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== p-timeout@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== dependencies: p-finally "^1.0.0" p-try@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== p-waterfall@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" + resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz" integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== dependencies: p-reduce "^2.0.0" -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= - dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - pacote@^11.2.6: version "11.3.5" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2" + resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz" integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== dependencies: "@npmcli/git" "^2.1.0" @@ -8626,14 +8553,14 @@ pacote@^11.2.6: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-glob@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + resolved "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz" integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= dependencies: glob-base "^0.3.0" @@ -8643,7 +8570,7 @@ parse-glob@^3.0.4: parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" @@ -8651,7 +8578,7 @@ parse-json@^4.0.0: parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -8661,7 +8588,7 @@ parse-json@^5.0.0, parse-json@^5.2.0: parse-path@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" + resolved "https://registry.npmjs.org/parse-path/-/parse-path-4.0.3.tgz" integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== dependencies: is-ssh "^1.3.0" @@ -8671,7 +8598,7 @@ parse-path@^4.0.0: parse-url@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" + resolved "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz" integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== dependencies: is-ssh "^1.3.0" @@ -8681,190 +8608,185 @@ parse-url@^6.0.0: parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== dependencies: parse5 "^6.0.1" parse5@6.0.1, parse5@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - path-is-network-drive@^1.0.13: version "1.0.13" - resolved "https://registry.yarnpkg.com/path-is-network-drive/-/path-is-network-drive-1.0.13.tgz#c9aa0183eb72c328aa83f43def93ddcb9d7ec4d4" + resolved "https://registry.npmjs.org/path-is-network-drive/-/path-is-network-drive-1.0.13.tgz" integrity sha512-Hg74mRN6mmXV+gTm3INjFK40ncAmC/Lo4qoQaSZ+GT3hZzlKdWQSqAjqyPeW0SvObP2W073WyYEBWY9d3wOm3A== dependencies: tslib "^2.3.1" -path-key@^2.0.0, path-key@^2.0.1: +path-key@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + 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.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-strip-sep@^1.0.10: version "1.0.10" - resolved "https://registry.yarnpkg.com/path-strip-sep/-/path-strip-sep-1.0.10.tgz#2be4e789406b298af8709ff79af716134b733b98" + resolved "https://registry.npmjs.org/path-strip-sep/-/path-strip-sep-1.0.10.tgz" integrity sha512-JpCy+8LAJQQTO1bQsb/84s1g+/Stm3h39aOpPRBQ/paMUGVPPZChLTOTKHoaCkc/6sKuF7yVsnq5Pe1S6xQGcA== dependencies: tslib "^2.3.1" path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pend@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picocolors@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pify@^2.0.0, pify@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pify@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + resolved "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== pinkie-promise@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pirates@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== dependencies: node-modules-regexp "^1.0.0" pirates@^4.0.4: version "4.0.4" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz" integrity sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw== pixelmatch@^5.1.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.2.1.tgz#9e4e4f4aa59648208a31310306a5bed5522b0d65" + resolved "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.2.1.tgz" integrity sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ== dependencies: pngjs "^4.0.1" "pkg-dir@< 6 >= 5": version "5.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz" integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== dependencies: find-up "^5.0.0" pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pngjs@^3.4.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" + resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== pngjs@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-4.0.1.tgz#f803869bb2fc1bfe1bf99aa4ec21c108117cfdbe" + resolved "https://registry.npmjs.org/pngjs/-/pngjs-4.0.1.tgz" integrity sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg== postcss-calc@^7.0.1: version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" + resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz" integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== dependencies: postcss "^7.0.27" @@ -8873,7 +8795,7 @@ postcss-calc@^7.0.1: postcss-colormin@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz" integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== dependencies: browserslist "^4.0.0" @@ -8884,7 +8806,7 @@ postcss-colormin@^4.0.3: postcss-convert-values@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz" integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== dependencies: postcss "^7.0.0" @@ -8892,35 +8814,35 @@ postcss-convert-values@^4.0.1: postcss-discard-comments@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz" integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== dependencies: postcss "^7.0.0" postcss-discard-duplicates@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz" integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== dependencies: postcss "^7.0.0" postcss-discard-empty@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz" integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== dependencies: postcss "^7.0.0" postcss-discard-overridden@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz" integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== dependencies: postcss "^7.0.0" postcss-easy-import@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-easy-import/-/postcss-easy-import-3.0.0.tgz#8eaaf5ae59566083d0cae98735dfd803e3ab194d" + resolved "https://registry.npmjs.org/postcss-easy-import/-/postcss-easy-import-3.0.0.tgz" integrity sha512-cfNsear/v8xlkl9v5Wm8y4Do/puiDQTFF+WX2Fo++h7oKt1fKWVVW/5Ca8hslYDQWnjndrg813cA23Pt1jsYdg== dependencies: globby "^6.1.0" @@ -8934,7 +8856,7 @@ postcss-easy-import@^3.0.0: postcss-import@^10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-10.0.0.tgz#4c85c97b099136cc5ea0240dc1dfdbfde4e2ebbe" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-10.0.0.tgz" integrity sha1-TIXJewmRNsxeoCQNwd/b/eTi674= dependencies: object-assign "^4.0.1" @@ -8945,7 +8867,7 @@ postcss-import@^10.0.0: postcss-load-config@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz" integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== dependencies: cosmiconfig "^5.0.0" @@ -8953,7 +8875,7 @@ postcss-load-config@^2.1.0: postcss-merge-longhand@^4.0.11: version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz" integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== dependencies: css-color-names "0.0.4" @@ -8963,7 +8885,7 @@ postcss-merge-longhand@^4.0.11: postcss-merge-rules@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz" integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== dependencies: browserslist "^4.0.0" @@ -8975,7 +8897,7 @@ postcss-merge-rules@^4.0.3: postcss-minify-font-values@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz" integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== dependencies: postcss "^7.0.0" @@ -8983,7 +8905,7 @@ postcss-minify-font-values@^4.0.2: postcss-minify-gradients@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz" integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== dependencies: cssnano-util-get-arguments "^4.0.0" @@ -8993,7 +8915,7 @@ postcss-minify-gradients@^4.0.2: postcss-minify-params@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz" integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== dependencies: alphanum-sort "^1.0.0" @@ -9005,7 +8927,7 @@ postcss-minify-params@^4.0.2: postcss-minify-selectors@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz" integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== dependencies: alphanum-sort "^1.0.0" @@ -9015,14 +8937,14 @@ postcss-minify-selectors@^4.0.2: postcss-modules-extract-imports@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" + resolved "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz" integrity sha1-thTJcgvmgW6u41+zpfqh26agXds= dependencies: postcss "^6.0.1" postcss-modules-local-by-default@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + resolved "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz" integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= dependencies: css-selector-tokenizer "^0.7.0" @@ -9030,7 +8952,7 @@ postcss-modules-local-by-default@1.2.0: postcss-modules-scope@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz" integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= dependencies: css-selector-tokenizer "^0.7.0" @@ -9038,7 +8960,7 @@ postcss-modules-scope@1.1.0: postcss-modules-values@1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + resolved "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz" integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= dependencies: icss-replace-symbols "^1.1.0" @@ -9046,7 +8968,7 @@ postcss-modules-values@1.3.0: postcss-modules@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-2.0.0.tgz#473d0d7326651d8408585c2a154115d5cb36cce0" + resolved "https://registry.npmjs.org/postcss-modules/-/postcss-modules-2.0.0.tgz" integrity sha512-eqp+Bva+U2cwQO7dECJ8/V+X+uH1HduNeITB0CPPFAu6d/8LKQ32/j+p9rQ2YL1QytVcrNU0X+fBqgGmQIA1Rw== dependencies: css-modules-loader-core "^1.1.0" @@ -9057,14 +8979,14 @@ postcss-modules@^2.0.0: postcss-normalize-charset@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz" integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== dependencies: postcss "^7.0.0" postcss-normalize-display-values@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz" integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== dependencies: cssnano-util-get-match "^4.0.0" @@ -9073,7 +8995,7 @@ postcss-normalize-display-values@^4.0.2: postcss-normalize-positions@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz" integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== dependencies: cssnano-util-get-arguments "^4.0.0" @@ -9083,7 +9005,7 @@ postcss-normalize-positions@^4.0.2: postcss-normalize-repeat-style@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz" integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== dependencies: cssnano-util-get-arguments "^4.0.0" @@ -9093,7 +9015,7 @@ postcss-normalize-repeat-style@^4.0.2: postcss-normalize-string@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz" integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== dependencies: has "^1.0.0" @@ -9102,7 +9024,7 @@ postcss-normalize-string@^4.0.2: postcss-normalize-timing-functions@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz" integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== dependencies: cssnano-util-get-match "^4.0.0" @@ -9111,7 +9033,7 @@ postcss-normalize-timing-functions@^4.0.2: postcss-normalize-unicode@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz" integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== dependencies: browserslist "^4.0.0" @@ -9120,7 +9042,7 @@ postcss-normalize-unicode@^4.0.1: postcss-normalize-url@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz" integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== dependencies: is-absolute-url "^2.0.0" @@ -9130,7 +9052,7 @@ postcss-normalize-url@^4.0.1: postcss-normalize-whitespace@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz" integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== dependencies: postcss "^7.0.0" @@ -9138,7 +9060,7 @@ postcss-normalize-whitespace@^4.0.2: postcss-ordered-values@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz" integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== dependencies: cssnano-util-get-arguments "^4.0.0" @@ -9147,7 +9069,7 @@ postcss-ordered-values@^4.1.2: postcss-reduce-initial@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz" integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== dependencies: browserslist "^4.0.0" @@ -9157,7 +9079,7 @@ postcss-reduce-initial@^4.0.3: postcss-reduce-transforms@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz" integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== dependencies: cssnano-util-get-match "^4.0.0" @@ -9167,7 +9089,7 @@ postcss-reduce-transforms@^4.0.2: postcss-selector-parser@^3.0.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz" integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== dependencies: dot-prop "^5.2.0" @@ -9176,7 +9098,7 @@ postcss-selector-parser@^3.0.0: postcss-selector-parser@^6.0.2: version "6.0.6" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz" integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== dependencies: cssesc "^3.0.0" @@ -9184,7 +9106,7 @@ postcss-selector-parser@^6.0.2: postcss-svgo@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" + resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz" integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw== dependencies: postcss "^7.0.0" @@ -9193,7 +9115,7 @@ postcss-svgo@^4.0.3: postcss-unique-selectors@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz" integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== dependencies: alphanum-sort "^1.0.0" @@ -9202,17 +9124,17 @@ postcss-unique-selectors@^4.0.1: postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== postcss-value-parser@^4.0.2: version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" + resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz" integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I= dependencies: chalk "^1.1.3" @@ -9221,7 +9143,7 @@ postcss@6.0.1: postcss@^6.0.1, postcss@^6.0.11: version "6.0.23" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz" integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== dependencies: chalk "^2.4.1" @@ -9230,7 +9152,7 @@ postcss@^6.0.1, postcss@^6.0.11: postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== dependencies: picocolors "^0.2.1" @@ -9238,32 +9160,27 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - preserve@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + resolved "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= prettier@2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== pretty-format@^23.6.0: version "23.6.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz" integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw== dependencies: ansi-regex "^3.0.0" @@ -9271,7 +9188,7 @@ pretty-format@^23.6.0: pretty-format@^27.0.0, pretty-format@^27.2.4: version "27.2.4" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.4.tgz#08ea39c5eab41b082852d7093059a091f6ddc748" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.2.4.tgz" integrity sha512-NUjw22WJHldzxyps2YjLZkUj6q1HvjqFezkB9Y2cklN8NtVZN/kZEXGZdFw4uny3oENzV5EEMESrkI0YDUH8vg== dependencies: "@jest/types" "^27.2.4" @@ -9281,7 +9198,7 @@ pretty-format@^27.0.0, pretty-format@^27.2.4: pretty-format@^27.5.1: version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: ansi-regex "^5.0.1" @@ -9290,22 +9207,22 @@ pretty-format@^27.5.1: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + 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.0, progress@^2.0.1: +progress@^2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-inflight@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= promise-retry@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== dependencies: err-code "^2.0.2" @@ -9313,12 +9230,12 @@ promise-retry@^2.0.1: promise.series@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" + resolved "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz" integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= prompts@^2.0.1: version "2.4.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== dependencies: kleur "^3.0.3" @@ -9326,24 +9243,24 @@ prompts@^2.0.1: promzard@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz" integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= dependencies: read "1" proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= protocols@^1.1.0, protocols@^1.4.0: version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" + resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== proxy-addr@~2.0.5: version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -9351,22 +9268,17 @@ proxy-addr@~2.0.5: proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.28, psl@^1.1.33: version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -9374,12 +9286,12 @@ pump@^3.0.0: punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== puppeteer@^1.15.0: version "1.20.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.20.0.tgz#e3d267786f74e1d87cf2d15acc59177f471bbe38" + resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz" integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ== dependencies: debug "^4.1.0" @@ -9393,7 +9305,7 @@ puppeteer@^1.15.0: puppeteer@^9.1.1: version "9.1.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-9.1.1.tgz#f74b7facf86887efd6c6b9fabb7baae6fdce012c" + resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" integrity sha512-W+nOulP2tYd/ZG99WuZC/I5ljjQQ7EUw/jQGcIb9eu8mDlZxNY2SgcJXTLG9h5gRvqA3uJOe4hZXYsd3EqioMw== dependencies: debug "^4.1.0" @@ -9411,29 +9323,29 @@ puppeteer@^9.1.1: q@^1.1.2, q@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@6.7.0: version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.9.4: version "6.10.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" + resolved "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz" integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== dependencies: side-channel "^1.0.4" qs@~6.5.2: version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== query-string@^6.13.8: version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz" integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: decode-uri-component "^0.2.0" @@ -9443,17 +9355,17 @@ query-string@^6.13.8: queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== randomatic@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + resolved "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz" integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== dependencies: is-number "^4.0.0" @@ -9462,19 +9374,19 @@ randomatic@^3.0.0: randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" @@ -9482,36 +9394,26 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.0.1, rc@^1.1.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-is@^17.0.1: version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== read-cache@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= dependencies: pify "^2.3.0" read-cmd-shim@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" + resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz" integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== read-package-json-fast@^2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== dependencies: json-parse-even-better-errors "^2.3.0" @@ -9519,7 +9421,7 @@ read-package-json-fast@^2.0.1: read-package-json@^2.0.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz" integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== dependencies: glob "^7.1.1" @@ -9529,7 +9431,7 @@ read-package-json@^2.0.0: read-package-json@^3.0.0, read-package-json@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-3.0.1.tgz#c7108f0b9390257b08c21e3004d2404c806744b9" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-3.0.1.tgz" integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== dependencies: glob "^7.1.1" @@ -9539,7 +9441,7 @@ read-package-json@^3.0.0, read-package-json@^3.0.1: read-package-tree@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + resolved "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz" integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== dependencies: read-package-json "^2.0.0" @@ -9548,7 +9450,7 @@ read-package-tree@^5.3.1: read-pkg-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz" integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= dependencies: find-up "^2.0.0" @@ -9556,7 +9458,7 @@ read-pkg-up@^3.0.0: read-pkg-up@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== dependencies: find-up "^4.1.0" @@ -9565,7 +9467,7 @@ read-pkg-up@^7.0.1: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" @@ -9574,7 +9476,7 @@ read-pkg@^3.0.0: read-pkg@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" @@ -9584,14 +9486,14 @@ read-pkg@^5.2.0: read@1, read@~1.0.1: version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -9600,7 +9502,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -9613,7 +9515,7 @@ readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: readdir-scoped-modules@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: debuglog "^1.0.1" @@ -9623,21 +9525,21 @@ readdir-scoped-modules@^1.0.0: readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" realpath-native@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + resolved "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz" integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== dependencies: util.promisify "^1.0.0" redent@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: indent-string "^4.0.0" @@ -9645,54 +9547,39 @@ redent@^3.0.0: regenerator-runtime@^0.11.0: version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== regex-cache@^0.4.2: version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + resolved "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz" integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== dependencies: is-equal-shallow "^0.1.3" -regexpp@^3.0.0, regexpp@^3.1.0: +regexpp@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -registry-auth-token@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" - integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= - dependencies: - rc "^1.0.1" - remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= repeat-element@^1.1.2: version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.5.2: version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= request@^2.88.0, request@^2.88.2: version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" @@ -9718,54 +9605,49 @@ request@^2.88.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + 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.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" + resolved "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz" integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve.exports@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@1.1.7: version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" @@ -9773,7 +9655,7 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19 resolve@^1.3.2: version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: is-core-module "^2.8.1" @@ -9782,7 +9664,7 @@ resolve@^1.3.2: restore-cursor@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" @@ -9790,7 +9672,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -9798,48 +9680,48 @@ restore-cursor@^3.1.0: retry@^0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rgb-regex@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz" integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= rgba-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rollup-plugin-css-only@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz#6a701cc5b051c6b3f0961e69b108a9a118e1b1df" + resolved "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz" integrity sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA== dependencies: "@rollup/pluginutils" "4" rollup-plugin-esbuild@^4.9.1: version "4.9.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-esbuild/-/rollup-plugin-esbuild-4.9.1.tgz#369d137e2b1542c8ee459495fd4f10de812666aa" + resolved "https://registry.npmjs.org/rollup-plugin-esbuild/-/rollup-plugin-esbuild-4.9.1.tgz" integrity sha512-qn/x7Wz9p3Xnva99qcb+nopH0d2VJwVnsxJTGEg+Sh2Z3tqQl33MhOwzekVo1YTKgv+yAmosjcBRJygMfGrtLw== dependencies: "@rollup/pluginutils" "^4.1.1" @@ -9850,14 +9732,14 @@ rollup-plugin-esbuild@^4.9.1: rollup-plugin-livereload@^2.0.0: version "2.0.5" - resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz#4747fa292a2cceb0c972c573d71b3d66b4252b37" + resolved "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz" integrity sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA== dependencies: livereload "^0.9.1" rollup-plugin-postcss@^3.1.1: version "3.1.8" - resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.8.tgz#d1bcaf8eb0fcb0936e3684c22dd8628d13a82fd1" + resolved "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-3.1.8.tgz" integrity sha512-JHnGfW8quNc6ePxEkZ05HEZ1YiRxDgY9RKEetMfsrwxR2kh/d90OVScTc6b1c2Q17Cs/5TRYL+1uddG21lQe3w== dependencies: chalk "^4.0.0" @@ -9877,7 +9759,7 @@ rollup-plugin-postcss@^3.1.1: rollup-plugin-rename-node-modules@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-rename-node-modules/-/rollup-plugin-rename-node-modules-1.3.1.tgz#d80091fc817ce726e7cdfc388ec2f4da286e280f" + resolved "https://registry.npmjs.org/rollup-plugin-rename-node-modules/-/rollup-plugin-rename-node-modules-1.3.1.tgz" integrity sha512-46TUPqO94GXuACYqVZjdbzNXTQAp+wTdZg/vUx2gaINb0da/ZPdaOtno2RGUOKBF4sbVM9v2ZqV98r4TQbp1UA== dependencies: estree-walker "^2.0.1" @@ -9885,7 +9767,7 @@ rollup-plugin-rename-node-modules@^1.3.1: rollup-plugin-svelte@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz#d45f2b92b1014be4eb46b55aa033fb9a9c65f04d" + resolved "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz" integrity sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg== dependencies: require-relative "^0.8.7" @@ -9893,7 +9775,7 @@ rollup-plugin-svelte@^7.1.0: rollup-plugin-terser@^7.0.2: version "7.0.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + resolved "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz" integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== dependencies: "@babel/code-frame" "^7.10.4" @@ -9903,7 +9785,7 @@ rollup-plugin-terser@^7.0.2: rollup-plugin-typescript2@^0.31.2: version "0.31.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.2.tgz#463aa713a7e2bf85b92860094b9f7fb274c5a4d8" + resolved "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.31.2.tgz" integrity sha512-hRwEYR1C8xDGVVMFJQdEVnNAeWRvpaY97g5mp3IeLnzhNXzSVq78Ye/BJ9PAaUfN4DXa/uDnqerifMOaMFY54Q== dependencies: "@rollup/pluginutils" "^4.1.2" @@ -9915,129 +9797,139 @@ rollup-plugin-typescript2@^0.31.2: rollup-plugin-web-worker-loader@^1.6.1: version "1.6.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-web-worker-loader/-/rollup-plugin-web-worker-loader-1.6.1.tgz#9d7a27575b64b0780fe4e8b3bc87470d217e485f" + resolved "https://registry.npmjs.org/rollup-plugin-web-worker-loader/-/rollup-plugin-web-worker-loader-1.6.1.tgz" integrity sha512-4QywQSz1NXFHKdyiou16mH3ijpcfLtLGOrAqvAqu1Gx+P8+zj+3gwC2BSL/VW1d+LW4nIHC8F7d7OXhs9UdR2A== rollup-pluginutils@^2.8.2: version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + resolved "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz" integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== dependencies: estree-walker "^0.6.1" rollup@^2.45.2: version "2.53.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.53.3.tgz#14b0e57f0874d4ad23bdbb13050cf70bcd1eabf7" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.53.3.tgz" integrity sha512-79QIGP5DXz5ZHYnCPi3tLz+elOQi6gudp9YINdaJdjG0Yddubo6JRFUM//qCZ0Bap/GJrsUoEBVdSOc4AkMlRA== optionalDependencies: fsevents "~2.3.2" rollup@^2.56.3: version "2.60.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.60.2.tgz#3f45ace36a9b10b4297181831ea0719922513463" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz" integrity sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw== optionalDependencies: fsevents "~2.3.2" rollup@^2.68.0: version "2.68.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.68.0.tgz#6ccabfd649447f8f21d62bf41662e5caece3bd66" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.68.0.tgz" integrity sha512-XrMKOYK7oQcTio4wyTz466mucnd8LzkiZLozZ4Rz0zQD+HeX4nUK4B8GrTX/2EvN2/vBF/i2WnaXboPxo0JylA== optionalDependencies: fsevents "~2.3.2" rollup@^2.71.1: version "2.71.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.71.1.tgz#82b259af7733dfd1224a8171013aaaad02971a22" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.71.1.tgz" integrity sha512-lMZk3XfUBGjrrZQpvPSoXcZSfKcJ2Bgn+Z0L1MoW2V8Wh7BVM+LOBJTPo16yul2MwL59cXedzW1ruq3rCjSRgw== optionalDependencies: fsevents "~2.3.2" run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-con@~1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/run-con/-/run-con-1.2.10.tgz#90de9d43d20274d00478f4c000495bd72f417d22" + integrity sha512-n7PZpYmMM26ZO21dd8y3Yw1TRtGABjRtgPSgFS/nhzfvbJMXFtJhJVyEgayMiP+w/23craJjsnfDvx4W4ue/HQ== + dependencies: + deep-extend "^0.6.0" + ini "~2.0.0" + minimist "^1.2.5" + strip-json-comments "~3.1.1" + run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" -rxjs@^6.4.0, rxjs@^6.6.0: +rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3: version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" sade@^1.4.0, sade@^1.7.4: version "1.7.4" - resolved "https://registry.yarnpkg.com/sade/-/sade-1.7.4.tgz#ea681e0c65d248d2095c90578c03ca0bb1b54691" + resolved "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz" integrity sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA== dependencies: mri "^1.1.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-identifier@^0.4.1: version "0.4.2" - resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" + resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@~1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== saxes@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== dependencies: xmlchars "^2.2.0" -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= - dependencies: - semver "^5.0.3" - -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +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== + dependencies: + lru-cache "^6.0.0" + send@0.17.1: version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" @@ -10056,14 +9948,14 @@ send@0.17.1: serialize-javascript@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== dependencies: randombytes "^2.1.0" serve-static@1.14.1: version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" @@ -10073,48 +9965,48 @@ serve-static@1.14.1: set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= setprototypeof@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -10123,19 +10015,19 @@ side-channel@^1.0.4: signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== simple-swizzle@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= dependencies: is-arrayish "^0.3.1" sirv-cli@^0.4.4: version "0.4.6" - resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-0.4.6.tgz#c28ab20deb3b34637f5a60863dc350f055abca04" + resolved "https://registry.npmjs.org/sirv-cli/-/sirv-cli-0.4.6.tgz" integrity sha512-/Vj85/kBvPL+n9ibgX6FicLE8VjidC1BhlX67PYPBfbBAphzR6i0k0HtU5c2arejfU3uzq8l3SYPCwl1x7z6Ww== dependencies: console-clear "^1.1.0" @@ -10148,7 +10040,7 @@ sirv-cli@^0.4.4: sirv@^0.4.6: version "0.4.6" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-0.4.6.tgz#185e44eb93d24009dd183b7494285c5180b81f22" + resolved "https://registry.npmjs.org/sirv/-/sirv-0.4.6.tgz" integrity sha512-rYpOXlNbpHiY4nVXxuDf4mXPvKz1reZGap/LkWp9TvcZ84qD/nPBjjH/6GZsgIjVMbOslnY8YYULAyP8jMn1GQ== dependencies: "@polka/url" "^0.5.0" @@ -10156,41 +10048,32 @@ sirv@^0.4.6: sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + 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.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= smart-buffer@^4.1.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz" integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== dependencies: agent-base "^6.0.2" @@ -10199,7 +10082,7 @@ socks-proxy-agent@^5.0.0: socks@^2.3.3: version "2.6.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz" integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== dependencies: ip "^1.1.5" @@ -10207,21 +10090,21 @@ socks@^2.3.3: sort-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= dependencies: is-plain-obj "^1.0.0" sort-keys@^4.0.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" + resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz" integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== dependencies: is-plain-obj "^2.0.0" source-map-support@^0.5.6, source-map-support@~0.5.19: version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" @@ -10229,27 +10112,32 @@ source-map-support@^0.5.6, source-map-support@~0.5.19: source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== sourcemap-codec@^1.4.4: version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -10257,12 +10145,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -10270,36 +10158,36 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.10" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz" integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== split-on-first@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== split2@^3.0.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" split@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" @@ -10314,53 +10202,53 @@ sshpk@^1.7.0: ssim.js@^3.1.1: version "3.5.0" - resolved "https://registry.yarnpkg.com/ssim.js/-/ssim.js-3.5.0.tgz#d7276b9ee99b57a5ff0db34035f02f35197e62df" + resolved "https://registry.npmjs.org/ssim.js/-/ssim.js-3.5.0.tgz" integrity sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g== ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== dependencies: minipass "^3.1.1" stable@^0.1.8: version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== stack-utils@^1.0.1: version "1.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz" integrity sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ== dependencies: escape-string-regexp "^2.0.0" stack-utils@^2.0.3: version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== dependencies: escape-string-regexp "^2.0.0" "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= strict-uri-encode@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= string-hash@^1.1.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" + resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= string-length@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -10368,16 +10256,16 @@ string-length@^4.0.1: string-width@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" @@ -10385,7 +10273,7 @@ string-width@^1.0.1: string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" @@ -10394,7 +10282,7 @@ string-width@^4.1.0, string-width@^4.2.0: string.prototype.trimend@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" @@ -10402,7 +10290,7 @@ string.prototype.trimend@^1.0.4: string.prototype.trimstart@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" @@ -10410,93 +10298,83 @@ string.prototype.trimstart@^1.0.4: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-ansi@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-indent@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - strip-outer@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" + resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== dependencies: escape-string-regexp "^1.0.2" strong-log-transformer@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz" integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== dependencies: duplexer "^0.1.1" @@ -10505,12 +10383,12 @@ strong-log-transformer@^2.1.0: style-inject@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3" + resolved "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz" integrity sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw== stylehacks@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz" integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== dependencies: browserslist "^4.0.0" @@ -10519,40 +10397,40 @@ stylehacks@^4.0.0: supports-color@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^3.2.3: version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= dependencies: has-flag "^1.0.0" supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== dependencies: has-flag "^4.0.0" @@ -10560,12 +10438,12 @@ supports-hyperlinks@^2.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" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== svelte-check@^1.4.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-1.6.0.tgz#fcc7b28252a89be0e4cd369c58bbf8e76e81295f" + resolved "https://registry.npmjs.org/svelte-check/-/svelte-check-1.6.0.tgz" integrity sha512-nQTlbFJWhwoeLY5rkhgbjzGQSwk5F1pRdEXait0EFaQSrE/iJF+PIjrQlk0BjL/ogk9HaR9ZI0DQSYrl7jl3IQ== dependencies: chalk "^4.0.0" @@ -10580,7 +10458,7 @@ svelte-check@^1.4.0: svelte-preprocess@^4.0.0: version "4.7.4" - resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-4.7.4.tgz#e4d5208ab25c2aaaf19e837f7d7bbf7930e61d2b" + resolved "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.7.4.tgz" integrity sha512-mDAmaltQl6e5zU2VEtoWEf7eLTfuOTGr9zt+BpA3AGHo8MIhKiNSPE9OLTCTOMgj0vj/uL9QBbaNmpG4G1CgIA== dependencies: "@types/pug" "^2.0.4" @@ -10590,12 +10468,12 @@ svelte-preprocess@^4.0.0: svelte@^3.2.0: version "3.40.0" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.40.0.tgz#777d47b0aad840bb4dd2eea74400e8caccfb7a5a" + resolved "https://registry.npmjs.org/svelte/-/svelte-3.40.0.tgz" integrity sha512-PCof5NCkxw7ZIkypiHwmjk8jCnnlmJ62NQIcGr/keBCOCx2FdAYmpjLjAey8hGy58xKK4WtwLUgFNcQZK2fPLQ== svgo@^1.0.0: version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz" integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== dependencies: chalk "^2.4.1" @@ -10614,24 +10492,12 @@ svgo@^1.0.0: symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + 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.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - tar-fs@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== dependencies: chownr "^1.1.1" @@ -10641,7 +10507,7 @@ tar-fs@^2.0.0: tar-stream@^2.1.4: version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: bl "^4.0.3" @@ -10652,7 +10518,7 @@ tar-stream@^2.1.4: tar@^4.4.12: version "4.4.19" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== dependencies: chownr "^1.1.4" @@ -10665,7 +10531,7 @@ tar@^4.4.12: tar@^6.0.2, tar@^6.1.0: version "6.1.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.8.tgz#4fc50cfe56511c538ce15b71e05eebe66530cbd4" + resolved "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz" integrity sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A== dependencies: chownr "^2.0.0" @@ -10677,12 +10543,12 @@ tar@^6.0.2, tar@^6.1.0: temp-dir@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= temp-write@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + resolved "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz" integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== dependencies: graceful-fs "^4.1.15" @@ -10691,16 +10557,9 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" - terminal-link@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -10708,7 +10567,7 @@ terminal-link@^2.0.0: terser@^5.0.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" + resolved "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz" integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== dependencies: commander "^2.20.0" @@ -10717,7 +10576,7 @@ terser@^5.0.0: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -10726,22 +10585,22 @@ test-exclude@^6.0.0: text-extensions@^1.0.0: version "1.9.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= throat@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@^2.0.0: version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: readable-stream "~2.3.6" @@ -10749,68 +10608,63 @@ through2@^2.0.0: through2@^4.0.0: version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== dependencies: readable-stream "3" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - timsort@^0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= tinydate@^1.0.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/tinydate/-/tinydate-1.3.0.tgz#e6ca8e5a22b51bb4ea1c3a2a4fd1352dbd4c57fb" + resolved "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz" integrity sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w== tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.x: version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toidentifier@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== tough-cookie@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: psl "^1.1.33" @@ -10819,7 +10673,7 @@ tough-cookie@^4.0.0: tough-cookie@~2.5.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" @@ -10827,36 +10681,41 @@ tough-cookie@~2.5.0: tr46@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== dependencies: punycode "^2.1.1" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-newlines@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== trim-off-newlines@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1" + resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz" integrity sha512-kh6Tu6GbeSNMGfrrZh6Bb/4ZEHV1QlB4xNDBeog8Y9/QwFlKTRyWvY3Fs9tRDAMZliVUwieMgEdIeL/FtqjkJg== trim-repeated@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" + resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= dependencies: escape-string-regexp "^1.0.2" ts-jest@^27.0.5: version "27.0.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.5.tgz#0b0604e2271167ec43c12a69770f0bb65ad1b750" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.5.tgz" integrity sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w== dependencies: bs-logger "0.x" @@ -10870,7 +10729,7 @@ ts-jest@^27.0.5: ts-jest@^27.1.3: version "27.1.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz" integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== dependencies: bs-logger "0.x" @@ -10884,7 +10743,7 @@ ts-jest@^27.1.3: ts-node@^10.7.0: version "10.7.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz" integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== dependencies: "@cspotcode/source-map-support" "0.7.0" @@ -10903,7 +10762,7 @@ ts-node@^10.7.0: ts-node@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz" integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== dependencies: arrify "^1.0.0" @@ -10917,37 +10776,22 @@ ts-node@^7.0.1: tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== tslib@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslint@^4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-4.5.1.tgz#05356871bef23a434906734006fc188336ba824b" - integrity sha1-BTVocb7yOkNJBnNABvwYgza6gks= - dependencies: - babel-code-frame "^6.20.0" - colors "^1.1.2" - diff "^3.0.1" - findup-sync "~0.3.0" - glob "^7.1.1" - optimist "~0.6.0" - resolve "^1.1.7" - tsutils "^1.1.0" - update-notifier "^2.0.0" - tslint@^6.1.3: version "6.1.3" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" + resolved "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz" integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== dependencies: "@babel/code-frame" "^7.0.0" @@ -10964,94 +10808,89 @@ tslint@^6.1.3: tslib "^1.13.0" tsutils "^2.29.0" -tsutils@^1.1.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0" - integrity sha1-ufmrROVa+WgYMdXyjQrur1x1DLA= - tsutils@^2.29.0: version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz" integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== dependencies: tslib "^1.8.1" -tsutils@^3.17.1: +tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== type-fest@^0.18.0: version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-fest@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz" integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== type-fest@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -11059,29 +10898,29 @@ type-is@~1.6.17, type-is@~1.6.18: typedarray-to-buffer@^3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== dependencies: is-typedarray "^1.0.0" typedarray@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@*: version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== typescript@^3.9.7: version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== typescript@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== typescript@^4.6.4: @@ -11089,24 +10928,29 @@ typescript@^4.6.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +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" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + uglify-js@^3.1.4: version "3.14.1" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz" integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== uid-number@0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= umask@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= unbox-primitive@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" @@ -11116,7 +10960,7 @@ unbox-primitive@^1.0.1: unbzip2-stream@^1.3.3: version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== dependencies: buffer "^5.2.1" @@ -11124,68 +10968,56 @@ unbzip2-stream@^1.3.3: uniq@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= uniqs@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= unique-filename@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz" integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= - dependencies: - crypto-random-string "^1.0.0" - universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unquote@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= - upath2@^3.1.12: version "3.1.12" - resolved "https://registry.yarnpkg.com/upath2/-/upath2-3.1.12.tgz#441b3dfbadde21731017bd1b7beb169498efd0a9" + resolved "https://registry.npmjs.org/upath2/-/upath2-3.1.12.tgz" integrity sha512-yC3eZeCyCXFWjy7Nu4pgjLhXNYjuzuUmJiRgSSw6TJp8Emc+E4951HGPJf+bldFC5SL7oBLeNbtm1fGzXn2gxw== dependencies: path-is-network-drive "^1.0.13" @@ -11194,54 +11026,31 @@ upath2@^3.1.12: upath@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" + resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-notifier@^2.0.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" - integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== - dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-ci "^1.0.10" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util-promisify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + resolved "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz" integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= dependencies: object.getownpropertydescriptors "^2.0.3" util.promisify@^1.0.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz" integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== dependencies: call-bind "^1.0.0" @@ -11252,7 +11061,7 @@ util.promisify@^1.0.0: util.promisify@~1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz" integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== dependencies: define-properties "^1.1.3" @@ -11262,27 +11071,27 @@ util.promisify@~1.0.0: utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache-lib@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz" integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz#0aeb763894f1a0a1676adf8a8b7612a38902446c" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz" integrity sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" @@ -11291,7 +11100,7 @@ v8-to-istanbul@^8.1.0: validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -11299,24 +11108,24 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: validate-npm-package-name@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= dependencies: builtins "^1.0.3" vary@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= vendors@^1.0.0: version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz" integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== verror@1.10.0: version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" @@ -11325,62 +11134,62 @@ verror@1.10.0: w3c-hr-time@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== dependencies: xml-name-validator "^3.0.0" walker@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= dependencies: makeerror "1.0.x" wcwidth@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= webidl-conversions@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== webidl-conversions@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== whatwg-encoding@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-mimetype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" @@ -11388,7 +11197,7 @@ whatwg-url@^5.0.0: whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== dependencies: lodash "^4.7.0" @@ -11397,7 +11206,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -11408,50 +11217,38 @@ which-boxed-primitive@^1.0.2: which@^1.2.9, which@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - dependencies: - string-width "^2.1.1" - word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -11460,12 +11257,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.0.0, write-file-atomic@^2.4.2: +write-file-atomic@^2.4.2: version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" @@ -11474,7 +11271,7 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.4.2: write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== dependencies: imurmurhash "^0.1.4" @@ -11484,7 +11281,7 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: write-json-file@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz" integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== dependencies: detect-indent "^5.0.0" @@ -11496,7 +11293,7 @@ write-json-file@^3.2.0: write-json-file@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-4.3.0.tgz#908493d6fd23225344af324016e4ca8f702dd12d" + resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz" integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== dependencies: detect-indent "^6.0.0" @@ -11508,7 +11305,7 @@ write-json-file@^4.3.0: write-pkg@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-4.0.0.tgz#675cc04ef6c11faacbbc7771b24c0abbf2a20039" + resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz" integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== dependencies: sort-keys "^2.0.0" @@ -11517,84 +11314,74 @@ write-pkg@^4.0.0: ws@^6.1.0: version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" ws@^7.2.3: version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== ws@^7.4.3, ws@^7.4.5: version "7.5.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz" integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== ws@^7.4.6: version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= - xml-name-validator@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xtend@~4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.0, yallist@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.0: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^18.1.3: version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -11602,7 +11389,7 @@ yargs-parser@^18.1.3: yargs@^16.2.0: version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -11615,7 +11402,7 @@ yargs@^16.2.0: yauzl@^2.10.0: version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" @@ -11623,15 +11410,15 @@ yauzl@^2.10.0: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yn@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + resolved "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz" integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 9da1e432cc68b05bd97547398628346faa912f45 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 31 May 2022 10:15:48 +0200 Subject: [PATCH 011/201] Fix #904 (#906) Properly remove crossorigin attribute --- packages/rrweb-snapshot/src/snapshot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 90ca8f3765..d6247e44a1 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -582,7 +582,7 @@ function serializeNode( } oldValue ? (attributes.crossOrigin = oldValue) - : delete attributes.crossOrigin; + : image.removeAttribute('crossorigin'); }; // The image content may not have finished loading yet. if (image.complete && image.naturalWidth !== 0) recordInlineImage(); From 9e249b9761e7ef67cb1680ff7e037253fda4e003 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Jun 2022 14:42:06 +0800 Subject: [PATCH 012/201] Bump minimist from 1.2.5 to 1.2.6 (#902) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 70 +++++++------------------------------------------------ 1 file changed, 9 insertions(+), 61 deletions(-) diff --git a/yarn.lock b/yarn.lock index 92bb7e13ef..4a130a685d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3072,11 +3072,6 @@ buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - builtin-modules@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz" @@ -3217,7 +3212,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3464,7 +3459,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.12.1, commander@^2.20.0: +commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5920,13 +5915,6 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" -is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== - dependencies: - has "^1.0.3" - is-date-object@^1.0.1: version "1.0.4" resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz" @@ -7851,9 +7839,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minipass-collect@^1.0.2: version "1.0.2" @@ -7951,7 +7939,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -8655,7 +8643,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.7: +path-parse@^1.0.6: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -9653,15 +9641,6 @@ 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.3.2: - version "1.22.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== - dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" @@ -9903,7 +9882,7 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10436,11 +10415,6 @@ 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.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" - 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" @@ -10774,7 +10748,7 @@ ts-node@^7.0.1: source-map-support "^0.5.6" yn "^2.0.0" -tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -10789,32 +10763,6 @@ tslib@^2.3.1: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tslint@^6.1.3: - version "6.1.3" - resolved "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz" - integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.13.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" From 058c4579b430977af0fdea0e0123cb126726876c Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 6 Jun 2022 16:55:15 +0200 Subject: [PATCH 013/201] Speed up snapshotting of many new dom nodes (#903) * Speed up snapshotting of many new dom nodes By avoiding reflow we shave about 15-25% off our snapshotting time * Improve newlyAddedElement docs * Optimize needMaskingText by using el.closest and less recursion * Serve all rrweb dist files * Split serializeNode into smaller functions Makes it easier to profile * Slow down cpu enhance tracing on fast machines * Increase timeout * Perf: only loop through ancestors when they have something to compare to * Perf: `hasNode` is cheaper than `getMeta` * Perf: If parents where already checked, no need to do it again * Perf: reverse for loops are faster Because they only do the .lenght check once. In this case I don't think we'll see much performance gains if any * Clean up code * Perf: check ancestors once with isBlocked * guessing this might fixes canvas test * Update packages/rrweb/src/record/observers/canvas/webgl.ts Co-authored-by: yz-yu * Fix #904 (#906) Properly remove crossorigin attribute * Bump minimist from 1.2.5 to 1.2.6 (#902) Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: yz-yu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .eslintrc.js | 6 +- package.json | 1 + packages/rrweb-snapshot/src/index.ts | 2 + packages/rrweb-snapshot/src/snapshot.ts | 626 ++++++++++-------- packages/rrweb-snapshot/src/utils.ts | 2 +- packages/rrweb-snapshot/test/snapshot.test.ts | 38 ++ packages/rrweb-snapshot/typings/index.d.ts | 4 +- packages/rrweb-snapshot/typings/snapshot.d.ts | 4 +- packages/rrweb/rollup.config.js | 13 + packages/rrweb/src/record/mutation.ts | 52 +- packages/rrweb/src/record/observer.ts | 12 +- .../rrweb/src/record/observers/canvas/2d.ts | 2 +- .../src/record/observers/canvas/canvas.ts | 5 +- .../src/record/observers/canvas/webgl.ts | 5 +- packages/rrweb/src/utils.ts | 48 +- .../test/__snapshots__/record.test.ts.snap | 120 ++++ .../rrweb/test/benchmark/dom-mutation.test.ts | 139 ++-- ...benchmark-dom-mutation-add-and-remove.html | 46 ++ packages/rrweb/test/record.test.ts | 17 + packages/rrweb/test/utils.ts | 6 +- packages/rrweb/typings/utils.d.ts | 2 +- yarn.lock | 37 +- 22 files changed, 815 insertions(+), 372 deletions(-) create mode 100644 packages/rrweb/test/html/benchmark-dom-mutation-add-and-remove.html diff --git a/.eslintrc.js b/.eslintrc.js index 994f4229b1..2f332b6bbd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,6 +16,8 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], }, - plugins: ['@typescript-eslint'], - rules: {}, + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], + rules: { + 'tsdoc/syntax': 'warn', + }, }; diff --git a/package.json b/package.json index eeb6276710..61c9b5227d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@typescript-eslint/parser": "^5.25.0", "concurrently": "^7.1.0", "eslint": "^8.15.0", + "eslint-plugin-tsdoc": "^0.2.16", "lerna": "^4.0.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", diff --git a/packages/rrweb-snapshot/src/index.ts b/packages/rrweb-snapshot/src/index.ts index 5bb5eea687..82dd6a42c3 100644 --- a/packages/rrweb-snapshot/src/index.ts +++ b/packages/rrweb-snapshot/src/index.ts @@ -4,6 +4,7 @@ import snapshot, { visitSnapshot, cleanupSnapshot, needMaskingText, + classMatchesRegex, IGNORED_NODE, } from './snapshot'; import rebuild, { @@ -25,5 +26,6 @@ export { visitSnapshot, cleanupSnapshot, needMaskingText, + classMatchesRegex, IGNORED_NODE, }; diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index d6247e44a1..1694d3f222 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -268,8 +268,7 @@ export function _isBlockedElement( return true; } } else { - // tslint:disable-next-line: prefer-for-of - for (let eIndex = 0; eIndex < element.classList.length; eIndex++) { + for (let eIndex = element.classList.length; eIndex--; ) { const className = element.classList[eIndex]; if (blockClass.test(className)) { return true; @@ -283,44 +282,50 @@ export function _isBlockedElement( return false; } -export function needMaskingText( +export function classMatchesRegex( node: Node | null, - maskTextClass: string | RegExp, - maskTextSelector: string | null, + regex: RegExp, + checkAncestors: boolean, ): boolean { - if (!node) { - return false; + if (!node) return false; + if (node.nodeType !== node.ELEMENT_NODE) { + if (!checkAncestors) return false; + return classMatchesRegex(node.parentNode, regex, checkAncestors); } - if (node.nodeType === node.ELEMENT_NODE) { - if (typeof maskTextClass === 'string') { - if ((node as HTMLElement).classList.contains(maskTextClass)) { - return true; - } - } else { - // tslint:disable-next-line: prefer-for-of - for ( - let eIndex = 0; - eIndex < (node as HTMLElement).classList.length; - eIndex++ - ) { - const className = (node as HTMLElement).classList[eIndex]; - if (maskTextClass.test(className)) { - return true; - } - } - } - if (maskTextSelector) { - if ((node as HTMLElement).matches(maskTextSelector)) { - return true; - } + + for (let eIndex = (node as HTMLElement).classList.length; eIndex--; ) { + const className = (node as HTMLElement).classList[eIndex]; + if (regex.test(className)) { + return true; } - return needMaskingText(node.parentNode, maskTextClass, maskTextSelector); } - if (node.nodeType === node.TEXT_NODE) { - // check parent node since text node do not have class name - return needMaskingText(node.parentNode, maskTextClass, maskTextSelector); + if (!checkAncestors) return false; + return classMatchesRegex(node.parentNode, regex, checkAncestors); +} + +export function needMaskingText( + node: Node, + maskTextClass: string | RegExp, + maskTextSelector: string | null, +): boolean { + const el: HTMLElement | null = + node.nodeType === node.ELEMENT_NODE + ? (node as HTMLElement) + : node.parentElement; + if (el === null) return false; + + if (typeof maskTextClass === 'string') { + if (el.classList.contains(maskTextClass)) return true; + if (el.closest(`.${maskTextClass}`)) return true; + } else { + if (classMatchesRegex(el, maskTextClass, true)) return true; + } + + if (maskTextSelector) { + if (el.matches(maskTextSelector)) return true; + if (el.closest(maskTextSelector)) return true; } - return needMaskingText(node.parentNode, maskTextClass, maskTextSelector); + return false; } // https://stackoverflow.com/a/36155560 @@ -389,6 +394,10 @@ function serializeNode( inlineImages: boolean; recordCanvas: boolean; keepIframeSrcFn: KeepIframeSrcFn; + /** + * `newlyAddedElement: true` skips scrollTop and scrollLeft check + */ + newlyAddedElement?: boolean; }, ): serializedNode | false { const { @@ -406,20 +415,17 @@ function serializeNode( inlineImages, recordCanvas, keepIframeSrcFn, + newlyAddedElement = false, } = options; // Only record root id when document object is not the base document - let rootId: number | undefined; - if (mirror.getMeta(doc)) { - const docId = mirror.getId(doc); - rootId = docId === 1 ? undefined : docId; - } + const rootId = getRootId(doc, mirror); switch (n.nodeType) { case n.DOCUMENT_NODE: - if ((n as HTMLDocument).compatMode !== 'CSS1Compat') { + if ((n as Document).compatMode !== 'CSS1Compat') { return { type: NodeType.Document, childNodes: [], - compatMode: (n as HTMLDocument).compatMode, // probably "BackCompat" + compatMode: (n as Document).compatMode, // probably "BackCompat" rootId, }; } else { @@ -438,245 +444,27 @@ function serializeNode( rootId, }; case n.ELEMENT_NODE: - const needBlock = _isBlockedElement( - n as HTMLElement, + return serializeElementNode(n as HTMLElement, { + doc, blockClass, blockSelector, - ); - const tagName = getValidTagName(n as HTMLElement); - let attributes: attributes = {}; - const len = (n as HTMLElement).attributes.length; - for (let i = 0; i < len; i++) { - const attr = (n as HTMLElement).attributes[i]; - attributes[attr.name] = transformAttribute( - doc, - tagName, - attr.name, - attr.value, - ); - } - // remote css - if (tagName === 'link' && inlineStylesheet) { - const stylesheet = Array.from(doc.styleSheets).find((s) => { - return s.href === (n as HTMLLinkElement).href; - }); - let cssText: string | null = null; - if (stylesheet) { - cssText = getCssRulesString(stylesheet ); - } - if (cssText) { - delete attributes.rel; - delete attributes.href; - attributes._cssText = absoluteToStylesheet( - cssText, - stylesheet!.href!, - ); - } - } - // dynamic stylesheet - if ( - tagName === 'style' && - (n as HTMLStyleElement).sheet && - // TODO: Currently we only try to get dynamic stylesheet when it is an empty style element - !( - (n as HTMLElement).innerText || - (n as HTMLElement).textContent || - '' - ).trim().length - ) { - const cssText = getCssRulesString( - (n as HTMLStyleElement).sheet as CSSStyleSheet, - ); - if (cssText) { - attributes._cssText = absoluteToStylesheet(cssText, getHref()); - } - } - // form fields - if ( - tagName === 'input' || - tagName === 'textarea' || - tagName === 'select' - ) { - const value = (n as HTMLInputElement | HTMLTextAreaElement).value; - if ( - attributes.type !== 'radio' && - attributes.type !== 'checkbox' && - attributes.type !== 'submit' && - attributes.type !== 'button' && - value - ) { - attributes.value = maskInputValue({ - type: attributes.type, - tagName, - value, - maskInputOptions, - maskInputFn, - }); - } else if ((n as HTMLInputElement).checked) { - attributes.checked = (n as HTMLInputElement).checked; - } - } - if (tagName === 'option') { - if ((n as HTMLOptionElement).selected && !maskInputOptions['select']) { - attributes.selected = true; - } else { - // ignore the html attribute (which corresponds to DOM (n as HTMLOptionElement).defaultSelected) - // if it's already been changed - delete attributes.selected; - } - } - // canvas image data - if (tagName === 'canvas' && recordCanvas) { - if ((n as ICanvas).__context === '2d') { - // only record this on 2d canvas - if (!is2DCanvasBlank(n as HTMLCanvasElement)) { - attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL( - dataURLOptions.type, - dataURLOptions.quality, - ); - } - } else if (!('__context' in n)) { - // context is unknown, better not call getContext to trigger it - const canvasDataURL = (n as HTMLCanvasElement).toDataURL( - dataURLOptions.type, - dataURLOptions.quality, - ); - - // create blank canvas of same dimensions - const blankCanvas = document.createElement('canvas'); - blankCanvas.width = (n as HTMLCanvasElement).width; - blankCanvas.height = (n as HTMLCanvasElement).height; - const blankCanvasDataURL = blankCanvas.toDataURL( - dataURLOptions.type, - dataURLOptions.quality, - ); - - // no need to save dataURL if it's the same as blank canvas - if (canvasDataURL !== blankCanvasDataURL) { - attributes.rr_dataURL = canvasDataURL; - } - } - } - // save image offline - if (tagName === 'img' && inlineImages) { - if (!canvasService) { - canvasService = doc.createElement('canvas'); - canvasCtx = canvasService.getContext('2d'); - } - const image = n as HTMLImageElement; - const oldValue = image.crossOrigin; - image.crossOrigin = 'anonymous'; - const recordInlineImage = () => { - try { - canvasService!.width = image.naturalWidth; - canvasService!.height = image.naturalHeight; - canvasCtx!.drawImage(image, 0, 0); - attributes.rr_dataURL = canvasService!.toDataURL( - dataURLOptions.type, - dataURLOptions.quality, - ); - } catch (err) { - console.warn( - `Cannot inline img src=${image.currentSrc}! Error: ${err}`, - ); - } - oldValue - ? (attributes.crossOrigin = oldValue) - : image.removeAttribute('crossorigin'); - }; - // The image content may not have finished loading yet. - if (image.complete && image.naturalWidth !== 0) recordInlineImage(); - else image.onload = recordInlineImage; - } - // media elements - if (tagName === 'audio' || tagName === 'video') { - attributes.rr_mediaState = (n as HTMLMediaElement).paused - ? 'paused' - : 'played'; - attributes.rr_mediaCurrentTime = (n as HTMLMediaElement).currentTime; - } - // scroll - if ((n as HTMLElement).scrollLeft) { - attributes.rr_scrollLeft = (n as HTMLElement).scrollLeft; - } - if ((n as HTMLElement).scrollTop) { - attributes.rr_scrollTop = (n as HTMLElement).scrollTop; - } - // block element - if (needBlock) { - const { width, height } = (n as HTMLElement).getBoundingClientRect(); - attributes = { - class: attributes.class, - rr_width: `${width}px`, - rr_height: `${height}px`, - }; - } - // iframe - if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src as string)) { - if (!(n as HTMLIFrameElement).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; - } - delete attributes.src; // prevent auto loading - } - return { - type: NodeType.Element, - tagName, - attributes, - childNodes: [], - isSVG: isSVGElement(n as Element) || undefined, - needBlock, + inlineStylesheet, + maskInputOptions, + maskInputFn, + dataURLOptions, + inlineImages, + recordCanvas, + keepIframeSrcFn, + newlyAddedElement, rootId, - }; + }); case n.TEXT_NODE: - // The parent node may not be a html element which has a tagName attribute. - // So just let it be undefined which is ok in this use case. - const parentTagName = - n.parentNode && (n.parentNode as HTMLElement).tagName; - let textContent = (n as Text).textContent; - const isStyle = parentTagName === 'STYLE' ? true : undefined; - const isScript = parentTagName === 'SCRIPT' ? true : undefined; - if (isStyle && textContent) { - try { - // try to read style sheet - if (n.nextSibling || n.previousSibling) { - // This is not the only child of the stylesheet. - // We can't read all of the sheet's .cssRules and expect them - // to _only_ include the current rule(s) added by the text node. - // So we'll be conservative and keep textContent as-is. - } else if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) { - textContent = stringifyStyleSheet( - (n.parentNode as HTMLStyleElement).sheet!, - ); - } - } catch (err) { - console.warn( - `Cannot get CSS styles from text's parentNode. Error: ${err}`, - n, - ); - } - textContent = absoluteToStylesheet(textContent, getHref()); - } - if (isScript) { - textContent = 'SCRIPT_PLACEHOLDER'; - } - if ( - !isStyle && - !isScript && - needMaskingText(n, maskTextClass, maskTextSelector) && - textContent - ) { - textContent = maskTextFn - ? maskTextFn(textContent) - : textContent.replace(/[\S]/g, '*'); - } - return { - type: NodeType.Text, - textContent: textContent || '', - isStyle, + return serializeTextNode(n as Text, { + maskTextClass, + maskTextSelector, + maskTextFn, rootId, - }; + }); case n.CDATA_SECTION_NODE: return { type: NodeType.CDATA, @@ -694,6 +482,290 @@ function serializeNode( } } +function getRootId(doc: Document, mirror: Mirror): number | undefined { + if (!mirror.hasNode(doc)) return undefined; + const docId = mirror.getId(doc); + return docId === 1 ? undefined : docId; +} + +function serializeTextNode( + n: Text, + options: { + maskTextClass: string | RegExp; + maskTextSelector: string | null; + maskTextFn: MaskTextFn | undefined; + rootId: number | undefined; + }, +): serializedNode { + const { maskTextClass, maskTextSelector, maskTextFn, rootId } = options; + // The parent node may not be a html element which has a tagName attribute. + // So just let it be undefined which is ok in this use case. + const parentTagName = n.parentNode && (n.parentNode as HTMLElement).tagName; + let textContent = n.textContent; + const isStyle = parentTagName === 'STYLE' ? true : undefined; + const isScript = parentTagName === 'SCRIPT' ? true : undefined; + if (isStyle && textContent) { + try { + // try to read style sheet + if (n.nextSibling || n.previousSibling) { + // This is not the only child of the stylesheet. + // We can't read all of the sheet's .cssRules and expect them + // to _only_ include the current rule(s) added by the text node. + // So we'll be conservative and keep textContent as-is. + } else if ((n.parentNode as HTMLStyleElement).sheet?.cssRules) { + textContent = stringifyStyleSheet( + (n.parentNode as HTMLStyleElement).sheet!, + ); + } + } catch (err) { + console.warn( + `Cannot get CSS styles from text's parentNode. Error: ${err}`, + n, + ); + } + textContent = absoluteToStylesheet(textContent, getHref()); + } + if (isScript) { + textContent = 'SCRIPT_PLACEHOLDER'; + } + if ( + !isStyle && + !isScript && + textContent && + needMaskingText(n, maskTextClass, maskTextSelector) + ) { + textContent = maskTextFn + ? maskTextFn(textContent) + : textContent.replace(/[\S]/g, '*'); + } + + return { + type: NodeType.Text, + textContent: textContent || '', + isStyle, + rootId, + }; +} + +function serializeElementNode( + n: HTMLElement, + options: { + doc: Document; + blockClass: string | RegExp; + blockSelector: string | null; + inlineStylesheet: boolean; + maskInputOptions: MaskInputOptions; + maskInputFn: MaskInputFn | undefined; + dataURLOptions?: DataURLOptions; + inlineImages: boolean; + recordCanvas: boolean; + keepIframeSrcFn: KeepIframeSrcFn; + /** + * `newlyAddedElement: true` skips scrollTop and scrollLeft check + */ + newlyAddedElement?: boolean; + rootId: number | undefined; + }, +): serializedNode | false { + const { + doc, + blockClass, + blockSelector, + inlineStylesheet, + maskInputOptions = {}, + maskInputFn, + dataURLOptions = {}, + inlineImages, + recordCanvas, + keepIframeSrcFn, + newlyAddedElement = false, + rootId, + } = options; + const needBlock = _isBlockedElement(n, blockClass, blockSelector); + const tagName = getValidTagName(n); + let attributes: attributes = {}; + const len = n.attributes.length; + for (let i = 0; i < len; i++) { + const attr = n.attributes[i]; + attributes[attr.name] = transformAttribute( + doc, + tagName, + attr.name, + attr.value, + ); + } + // remote css + if (tagName === 'link' && inlineStylesheet) { + const stylesheet = Array.from(doc.styleSheets).find((s) => { + return s.href === (n as HTMLLinkElement).href; + }); + let cssText: string | null = null; + if (stylesheet) { + cssText = getCssRulesString(stylesheet); + } + if (cssText) { + delete attributes.rel; + delete attributes.href; + attributes._cssText = absoluteToStylesheet(cssText, stylesheet!.href!); + } + } + // dynamic stylesheet + if ( + tagName === 'style' && + (n as HTMLStyleElement).sheet && + // TODO: Currently we only try to get dynamic stylesheet when it is an empty style element + !(n.innerText || n.textContent || '').trim().length + ) { + const cssText = getCssRulesString( + (n as HTMLStyleElement).sheet as CSSStyleSheet, + ); + if (cssText) { + attributes._cssText = absoluteToStylesheet(cssText, getHref()); + } + } + // form fields + if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') { + const value = (n as HTMLInputElement | HTMLTextAreaElement).value; + if ( + attributes.type !== 'radio' && + attributes.type !== 'checkbox' && + attributes.type !== 'submit' && + attributes.type !== 'button' && + value + ) { + attributes.value = maskInputValue({ + type: attributes.type, + tagName, + value, + maskInputOptions, + maskInputFn, + }); + } else if ((n as HTMLInputElement).checked) { + attributes.checked = (n as HTMLInputElement).checked; + } + } + if (tagName === 'option') { + if ((n as HTMLOptionElement).selected && !maskInputOptions['select']) { + attributes.selected = true; + } else { + // ignore the html attribute (which corresponds to DOM (n as HTMLOptionElement).defaultSelected) + // if it's already been changed + delete attributes.selected; + } + } + // canvas image data + if (tagName === 'canvas' && recordCanvas) { + if ((n as ICanvas).__context === '2d') { + // only record this on 2d canvas + if (!is2DCanvasBlank(n as HTMLCanvasElement)) { + attributes.rr_dataURL = (n as HTMLCanvasElement).toDataURL( + dataURLOptions.type, + dataURLOptions.quality, + ); + } + } else if (!('__context' in n)) { + // context is unknown, better not call getContext to trigger it + const canvasDataURL = (n as HTMLCanvasElement).toDataURL( + dataURLOptions.type, + dataURLOptions.quality, + ); + + // create blank canvas of same dimensions + const blankCanvas = document.createElement('canvas'); + blankCanvas.width = (n as HTMLCanvasElement).width; + blankCanvas.height = (n as HTMLCanvasElement).height; + const blankCanvasDataURL = blankCanvas.toDataURL( + dataURLOptions.type, + dataURLOptions.quality, + ); + + // no need to save dataURL if it's the same as blank canvas + if (canvasDataURL !== blankCanvasDataURL) { + attributes.rr_dataURL = canvasDataURL; + } + } + } + // save image offline + if (tagName === 'img' && inlineImages) { + if (!canvasService) { + canvasService = doc.createElement('canvas'); + canvasCtx = canvasService.getContext('2d'); + } + const image = n as HTMLImageElement; + const oldValue = image.crossOrigin; + image.crossOrigin = 'anonymous'; + const recordInlineImage = () => { + try { + canvasService!.width = image.naturalWidth; + canvasService!.height = image.naturalHeight; + canvasCtx!.drawImage(image, 0, 0); + attributes.rr_dataURL = canvasService!.toDataURL( + dataURLOptions.type, + dataURLOptions.quality, + ); + } catch (err) { + console.warn( + `Cannot inline img src=${image.currentSrc}! Error: ${err}`, + ); + } + oldValue + ? (attributes.crossOrigin = oldValue) + : image.removeAttribute('crossorigin'); + }; + // The image content may not have finished loading yet. + if (image.complete && image.naturalWidth !== 0) recordInlineImage(); + else image.onload = recordInlineImage; + } + // media elements + if (tagName === 'audio' || tagName === 'video') { + attributes.rr_mediaState = (n as HTMLMediaElement).paused + ? 'paused' + : 'played'; + attributes.rr_mediaCurrentTime = (n as HTMLMediaElement).currentTime; + } + // Scroll + if (!newlyAddedElement) { + // `scrollTop` and `scrollLeft` are expensive calls because they trigger reflow. + // Since `scrollTop` & `scrollLeft` are always 0 when an element is added to the DOM. + // And scrolls also get picked up by rrweb's ScrollObserver + // So we can safely skip the `scrollTop/Left` calls for newly added elements + if (n.scrollLeft) { + attributes.rr_scrollLeft = n.scrollLeft; + } + if (n.scrollTop) { + attributes.rr_scrollTop = n.scrollTop; + } + } + // block element + if (needBlock) { + const { width, height } = n.getBoundingClientRect(); + attributes = { + class: attributes.class, + rr_width: `${width}px`, + rr_height: `${height}px`, + }; + } + // iframe + if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src as string)) { + if (!(n as HTMLIFrameElement).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; + } + delete attributes.src; // prevent auto loading + } + + return { + type: NodeType.Element, + tagName, + attributes, + childNodes: [], + isSVG: isSVGElement(n as Element) || undefined, + needBlock, + rootId, + }; +} + function lowerIfExists(maybeAttr: string | number | boolean): string { if (maybeAttr === undefined) { return ''; @@ -819,6 +891,7 @@ export function serializeNodeWithId( node: serializedNodeWithId, ) => unknown; iframeLoadTimeout?: number; + newlyAddedElement?: boolean; }, ): serializedNodeWithId | null { const { @@ -841,6 +914,7 @@ export function serializeNodeWithId( onIframeLoad, iframeLoadTimeout = 5000, keepIframeSrcFn = () => false, + newlyAddedElement = false, } = options; let { preserveWhiteSpace = true } = options; const _serializedNode = serializeNode(n, { @@ -858,6 +932,7 @@ export function serializeNodeWithId( inlineImages, recordCanvas, keepIframeSrcFn, + newlyAddedElement, }); if (!_serializedNode) { // TODO: dev only @@ -1109,6 +1184,7 @@ function snapshot( onIframeLoad, iframeLoadTimeout, keepIframeSrcFn, + newlyAddedElement: false, }); } diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index a2bde1570c..006bb2724f 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -13,7 +13,7 @@ export function isElement(n: Node): n is Element { export function isShadowRoot(n: Node): n is ShadowRoot { const host: Element | null = (n as ShadowRoot)?.host; - return Boolean(host && host.shadowRoot && host.shadowRoot === n); + return Boolean(host?.shadowRoot === n); } export class Mirror implements IMirror { diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index 5b05b7cafd..75d635e0c0 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -180,3 +180,41 @@ describe('style elements', () => { }); }); }); + +describe('scrollTop/scrollLeft', () => { + const serializeNode = (node: Node): serializedNodeWithId | null => { + return serializeNodeWithId(node, { + doc: document, + mirror: new Mirror(), + blockClass: 'blockblock', + blockSelector: null, + maskTextClass: 'maskmask', + maskTextSelector: null, + skipChild: false, + inlineStylesheet: true, + maskTextFn: undefined, + maskInputFn: undefined, + slimDOMOptions: {}, + newlyAddedElement: false, + }); + }; + + const render = (html: string): HTMLDivElement => { + document.write(html); + return document.querySelector('div')!; + }; + + it('should serialize scroll positions', () => { + const el = render(`
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +
`); + el.scrollTop = 10; + el.scrollLeft = 20; + expect(serializeNode(el)).toMatchObject({ + attributes: { + rr_scrollTop: 10, + rr_scrollLeft: 20, + }, + }); + }); +}); diff --git a/packages/rrweb-snapshot/typings/index.d.ts b/packages/rrweb-snapshot/typings/index.d.ts index efd1fb347e..194b8d3ad7 100644 --- a/packages/rrweb-snapshot/typings/index.d.ts +++ b/packages/rrweb-snapshot/typings/index.d.ts @@ -1,5 +1,5 @@ -import snapshot, { serializeNodeWithId, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, IGNORED_NODE } from './snapshot'; +import snapshot, { serializeNodeWithId, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, classMatchesRegex, IGNORED_NODE } from './snapshot'; import rebuild, { buildNodeWithSN, addHoverClass, createCache } from './rebuild'; export * from './types'; export * from './utils'; -export { snapshot, serializeNodeWithId, rebuild, buildNodeWithSN, addHoverClass, createCache, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, IGNORED_NODE, }; +export { snapshot, serializeNodeWithId, rebuild, buildNodeWithSN, addHoverClass, createCache, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, classMatchesRegex, IGNORED_NODE, }; diff --git a/packages/rrweb-snapshot/typings/snapshot.d.ts b/packages/rrweb-snapshot/typings/snapshot.d.ts index 0cdca439df..72a7c4ca20 100644 --- a/packages/rrweb-snapshot/typings/snapshot.d.ts +++ b/packages/rrweb-snapshot/typings/snapshot.d.ts @@ -5,7 +5,8 @@ export declare function absoluteToStylesheet(cssText: string | null, href: strin export declare function absoluteToDoc(doc: Document, attributeValue: string): string; export declare function transformAttribute(doc: Document, tagName: string, name: string, value: string): string; export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null): boolean; -export declare function needMaskingText(node: Node | null, maskTextClass: string | RegExp, maskTextSelector: string | null): boolean; +export declare function classMatchesRegex(node: Node | null, regex: RegExp, checkAncestors: boolean): boolean; +export declare function needMaskingText(node: Node, maskTextClass: string | RegExp, maskTextSelector: string | null): boolean; export declare function serializeNodeWithId(n: Node, options: { doc: Document; mirror: Mirror; @@ -27,6 +28,7 @@ export declare function serializeNodeWithId(n: Node, options: { onSerialize?: (n: Node) => unknown; onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedNodeWithId) => unknown; iframeLoadTimeout?: number; + newlyAddedElement?: boolean; }): serializedNodeWithId | null; declare function snapshot(n: Document, options?: { mirror?: Mirror; diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index fe4ec73171..008767236a 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -213,6 +213,19 @@ if (process.env.BROWSER_ONLY) { configs = []; + // browser record + replay, unminified (for profiling and performance testing) + configs.push({ + input: './src/index.ts', + plugins: getPlugins(), + output: [ + { + name: 'rrweb', + format: 'iife', + file: pkg.unpkg, + }, + ], + }); + for (const c of browserOnlyBaseConfigs) { configs.push({ input: c.input, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 91272adda2..aef3aa4cb4 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -308,6 +308,7 @@ export default class MutationBuffer { this.iframeManager.attachIframe(iframe, childSn, this.mirror); this.shadowDomManager.observeAttachShadow(iframe); }, + newlyAddedElement: true, }); if (sn) { adds.push({ @@ -432,7 +433,10 @@ export default class MutationBuffer { switch (m.type) { case 'characterData': { const value = m.target.textContent; - if (!isBlocked(m.target, this.blockClass) && value !== m.oldValue) { + if ( + !isBlocked(m.target, this.blockClass, false) && + value !== m.oldValue + ) { this.texts.push({ value: needMaskingText( @@ -461,7 +465,10 @@ export default class MutationBuffer { maskInputFn: this.maskInputFn, }); } - if (isBlocked(m.target, this.blockClass) || value === m.oldValue) { + if ( + isBlocked(m.target, this.blockClass, false) || + value === m.oldValue + ) { return; } let item: attributeCursor | undefined = this.attributes.find( @@ -518,6 +525,11 @@ export default class MutationBuffer { break; } case 'childList': { + /** + * Parent is blocked, ignore all child mutations + */ + if (isBlocked(m.target, this.blockClass, true)) return; + m.addedNodes.forEach((n) => this.genAdds(n, m.target)); m.removedNodes.forEach((n) => { const nodeId = this.mirror.getId(n); @@ -525,7 +537,7 @@ export default class MutationBuffer { ? this.mirror.getId(m.target.host) : this.mirror.getId(m.target); if ( - isBlocked(m.target, this.blockClass) || + isBlocked(m.target, this.blockClass, false) || isIgnored(n, this.mirror) || !isSerialized(n, this.mirror) ) { @@ -571,19 +583,17 @@ export default class MutationBuffer { } }; + /** + * Make sure you check if `n`'s parent is blocked before calling this function + * */ private genAdds = (n: Node, target?: Node) => { - // parent was blocked, so we can ignore this node - if (target && isBlocked(target, this.blockClass)) { - return; - } - - if (this.mirror.getMeta(n)) { + if (this.mirror.hasNode(n)) { if (isIgnored(n, this.mirror)) { return; } this.movedSet.add(n); let targetId: number | null = null; - if (target && this.mirror.getMeta(target)) { + if (target && this.mirror.hasNode(target)) { targetId = this.mirror.getId(target); } if (targetId && targetId !== -1) { @@ -596,8 +606,8 @@ export default class MutationBuffer { // if this node is blocked `serializeNode` will turn it into a placeholder element // but we have to remove it's children otherwise they will be added as placeholders too - if (!isBlocked(n, this.blockClass)) - (n ).childNodes.forEach((childN) => this.genAdds(childN)); + if (!isBlocked(n, this.blockClass, false)) + n.childNodes.forEach((childN) => this.genAdds(childN)); }; } @@ -616,6 +626,15 @@ function isParentRemoved( removes: removedNodeMutation[], n: Node, mirror: Mirror, +): boolean { + if (removes.length === 0) return false; + return _isParentRemoved(removes, n, mirror); +} + +function _isParentRemoved( + removes: removedNodeMutation[], + n: Node, + mirror: Mirror, ): boolean { const { parentNode } = n; if (!parentNode) { @@ -625,10 +644,15 @@ function isParentRemoved( if (removes.some((r) => r.id === parentId)) { return true; } - return isParentRemoved(removes, parentNode, mirror); + return _isParentRemoved(removes, parentNode, mirror); } function isAncestorInSet(set: Set, n: Node): boolean { + if (set.size === 0) return false; + return _isAncestorInSet(set, n); +} + +function _isAncestorInSet(set: Set, n: Node): boolean { const { parentNode } = n; if (!parentNode) { return false; @@ -636,5 +660,5 @@ function isAncestorInSet(set: Set, n: Node): boolean { if (set.has(parentNode)) { return true; } - return isAncestorInSet(set, parentNode); + return _isAncestorInSet(set, parentNode); } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index fe5830b6b6..72a6043f54 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -221,7 +221,7 @@ function initMouseInteractionObserver({ const getHandler = (eventKey: keyof typeof MouseInteractions) => { return (event: MouseEvent | TouchEvent) => { const target = getEventTarget(event) as Node; - if (isBlocked(target, blockClass)) { + if (isBlocked(target, blockClass, true)) { return; } const e = isTouchEvent(event) ? event.changedTouches[0] : event; @@ -267,7 +267,7 @@ export function initScrollObserver({ >): listenerHandler { const updatePosition = throttle((evt) => { const target = getEventTarget(evt); - if (!target || isBlocked(target as Node, blockClass)) { + if (!target || isBlocked(target as Node, blockClass, true)) { return; } const id = mirror.getId(target as Node); @@ -344,7 +344,7 @@ function initInputObserver({ !target || !(target as Element).tagName || INPUT_TAGS.indexOf((target as Element).tagName) < 0 || - isBlocked(target as Node, blockClass) + isBlocked(target as Node, blockClass, true) ) { return; } @@ -549,8 +549,8 @@ function initStyleSheetObserver( Object.entries(supportedNestedCSSRuleTypes).forEach(([typeKey, type]) => { unmodifiedFunctions[typeKey] = { - insertRule: (type ).prototype.insertRule, - deleteRule: (type ).prototype.deleteRule, + insertRule: type.prototype.insertRule, + deleteRule: type.prototype.deleteRule, }; type.prototype.insertRule = function (rule: string, index?: number) { @@ -653,7 +653,7 @@ function initMediaInteractionObserver({ const handler = (type: MediaInteractions) => throttle((event: Event) => { const target = getEventTarget(event); - if (!target || isBlocked(target as Node, blockClass)) { + if (!target || isBlocked(target as Node, blockClass, true)) { return; } const { currentTime, volume, muted } = target as HTMLMediaElement; diff --git a/packages/rrweb/src/record/observers/canvas/2d.ts b/packages/rrweb/src/record/observers/canvas/2d.ts index 6309a69e3c..488929aff3 100644 --- a/packages/rrweb/src/record/observers/canvas/2d.ts +++ b/packages/rrweb/src/record/observers/canvas/2d.ts @@ -36,7 +36,7 @@ export default function initCanvas2DMutationObserver( this: CanvasRenderingContext2D, ...args: Array ) { - if (!isBlocked(this.canvas, blockClass)) { + if (!isBlocked(this.canvas, blockClass, true)) { // Using setTimeout as toDataURL can be heavy // and we'd rather not block the main thread setTimeout(() => { diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index 7a5f264c5b..fe882c7315 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -17,9 +17,8 @@ export default function initCanvasContextObserver( contextType: string, ...args: Array ) { - if (!isBlocked(this, blockClass)) { - if (!('__context' in this)) - (this ).__context = contextType; + if (!isBlocked(this, blockClass, true)) { + if (!('__context' in this)) this.__context = contextType; } return original.apply(this, [contextType, ...args]); }; diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index 3a70500031..89390f1e40 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -31,8 +31,7 @@ function patchGLPrototype( return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); saveWebGLVar(result, win, prototype); - if (!isBlocked(this.canvas , blockClass)) { - const id = mirror.getId(this.canvas ); + if (!isBlocked(this.canvas, blockClass, true)) { const recordArgs = serializeArgs([...args], win, prototype); const mutation: canvasMutationWithType = { @@ -41,7 +40,7 @@ function patchGLPrototype( args: recordArgs, }; // TODO: this could potentially also be an OffscreenCanvas as well as HTMLCanvasElement - cb(this.canvas , mutation); + cb(this.canvas, mutation); } return result; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 0b546b5fe7..ea8cd1f1b8 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -10,7 +10,7 @@ import type { textMutation, } from './types'; import type { IMirror, Mirror } from 'rrweb-snapshot'; -import { isShadowRoot, IGNORED_NODE } from 'rrweb-snapshot'; +import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot'; import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom'; export function on( @@ -180,32 +180,34 @@ export function getWindowWidth(): number { ); } -export function isBlocked(node: Node | null, blockClass: blockClass): boolean { +/** + * Checks if the given element set to be blocked by rrweb + * @param node - node to check + * @param blockClass - class name to check + * @param ignoreParents - whether to search through parent nodes for the block class + * @returns true/false if the node was blocked or not + */ +export function isBlocked( + node: Node | null, + blockClass: blockClass, + checkAncestors: boolean, +): boolean { if (!node) { return false; } - if (node.nodeType === node.ELEMENT_NODE) { - let needBlock = false; - if (typeof blockClass === 'string') { - if ((node as HTMLElement).closest !== undefined) { - return (node as HTMLElement).closest('.' + blockClass) !== null; - } else { - needBlock = (node as HTMLElement).classList.contains(blockClass); - } - } else { - (node as HTMLElement).classList.forEach((className) => { - if (blockClass.test(className)) { - needBlock = true; - } - }); - } - return needBlock || isBlocked(node.parentNode, blockClass); - } - if (node.nodeType === node.TEXT_NODE) { - // check parent node since text node do not have class name - return isBlocked(node.parentNode, blockClass); + const el: HTMLElement | null = + node.nodeType === node.ELEMENT_NODE + ? (node as HTMLElement) + : node.parentElement; + if (!el) return false; + + if (typeof blockClass === 'string') { + if (el.classList.contains(blockClass)) return true; + if (checkAncestors && el.closest('.' + blockClass) !== null) return true; + } else { + if (classMatchesRegex(el, blockClass, checkAncestors)) return true; } - return isBlocked(node.parentNode, blockClass); + return false; } export function isSerialized(n: Node, mirror: Mirror): boolean { diff --git a/packages/rrweb/test/__snapshots__/record.test.ts.snap b/packages/rrweb/test/__snapshots__/record.test.ts.snap index 74c951afa8..e65b2372c8 100644 --- a/packages/rrweb/test/__snapshots__/record.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/record.test.ts.snap @@ -1131,6 +1131,126 @@ exports[`record is safe to checkout during async callbacks 1`] = ` ]" `; +exports[`record should record scroll position 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 6 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\", + \\"size\\": \\"40\\" + }, + \\"childNodes\\": [], + \\"id\\": 7 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 8 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 5, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": { + \\"style\\": \\"overflow: auto; height: Npx; width: Npx;\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + } + }, + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"testtesttesttesttesttesttesttesttesttest\\", + \\"id\\": 10 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 3, + \\"id\\": 9, + \\"x\\": 10, + \\"y\\": 10 + } + } +]" +`; + exports[`record without CSSGroupingRule support captures nested stylesheet rules 1`] = ` "[ { diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts index d2883ad0ae..8e446facca 100644 --- a/packages/rrweb/test/benchmark/dom-mutation.test.ts +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -1,15 +1,43 @@ // tslint:disable:no-console no-any import * as fs from 'fs'; import * as path from 'path'; +import type { Page } from 'puppeteer'; import type { eventWithTime, recordOptions } from '../../src/types'; -import { startServer, launchPuppeteer, replaceLast, ISuite } from '../utils'; +import { startServer, launchPuppeteer, ISuite, getServerURL } from '../utils'; + +const suites: Array< + { + title: string; + eval: string; + times?: number; // defaults to 5 + } & ({ html: string } | { url: string }) +> = [ + // { + // title: 'benchmarking external website', + // url: 'http://localhost:5050', + // eval: 'document.querySelector("button").click()', + // times: 10, + // }, + { + title: 'create 1000x10 DOM nodes', + html: 'benchmark-dom-mutation.html', + eval: 'window.workload()', + times: 10, + }, + { + title: 'create 1000x10x2 DOM nodes and remove a bunch of them', + html: 'benchmark-dom-mutation-add-and-remove.html', + eval: 'window.workload()', + times: 10, + }, +]; function avg(v: number[]): number { return v.reduce((prev, cur) => prev + cur, 0) / v.length; } describe('benchmark: mutation observer', () => { - let code: ISuite['code']; + jest.setTimeout(240000); let page: ISuite['page']; let browser: ISuite['browser']; let server: ISuite['server']; @@ -20,9 +48,6 @@ describe('benchmark: mutation observer', () => { dumpio: true, headless: true, }); - - const bundlePath = path.resolve(__dirname, '../../dist/rrweb.min.js'); - code = fs.readFileSync(bundlePath, 'utf8'); }); afterEach(async () => { @@ -36,30 +61,19 @@ describe('benchmark: mutation observer', () => { const getHtml = (fileName: string): string => { const filePath = path.resolve(__dirname, `../html/${fileName}`); - const html = fs.readFileSync(filePath, 'utf8'); - return replaceLast( - html, - '', - ` - - - `, - ); + return fs.readFileSync(filePath, 'utf8'); }; - const suites: { - title: string; - html: string; - times?: number; // default to 5 - }[] = [ - { - title: 'create 1000x10 DOM nodes', - html: 'benchmark-dom-mutation.html', - times: 10, - }, - ]; + const addRecordingScript = async (page: Page) => { + // const scriptUrl = `${getServerURL(server)}/rrweb-1.1.3.js`; + const scriptUrl = `${getServerURL(server)}/rrweb.js`; + await page.evaluate((url) => { + const scriptEl = document.createElement('script'); + scriptEl.src = url; + document.head.append(scriptEl); + }, scriptUrl); + await page.waitForFunction('window.rrweb'); + }; for (const suite of suites) { it(suite.title, async () => { @@ -68,12 +82,19 @@ describe('benchmark: mutation observer', () => { console.log(`${message.type().toUpperCase()} ${message.text()}`), ); - const times = suite.times ?? 5; - const durations: number[] = []; - for (let i = 0; i < times; i++) { - await page.goto('about:blank'); - await page.setContent(getHtml.call(this, suite.html)); - const duration = (await page.evaluate(() => { + const loadPage = async () => { + if ('html' in suite) { + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, suite.html)); + } else { + await page.goto(suite.url); + } + + await addRecordingScript(page); + }; + + const getDuration = async (): Promise => { + return (await page.evaluate((triggerWorkloadScript) => { return new Promise((resolve, reject) => { let start = 0; let lastEvent: eventWithTime | null; @@ -94,14 +115,55 @@ describe('benchmark: mutation observer', () => { const record = (window as any).rrweb.record; record(options); - (window as any).workload(); - start = Date.now(); - setTimeout(() => { + eval(triggerWorkloadScript); + + requestAnimationFrame(() => { record.addCustomEvent('FTAG', {}); - }, 0); + }); }); - })) as number; + }, suite.eval)) as number; + }; + + // generate profile.json file + const profileFilename = `profile-${new Date().toISOString()}.json`; + const tempDirectory = path.resolve(path.join(__dirname, '../../temp')); + fs.mkdirSync(tempDirectory, { recursive: true }); + const profilePath = path.resolve(tempDirectory, profileFilename); + + const client = await page.target().createCDPSession(); + await client.send('Emulation.setCPUThrottlingRate', { rate: 6 }); + + await page.tracing.start({ + path: profilePath, + screenshots: true, + categories: [ + '-*', + 'devtools.timeline', + 'v8.execute', + 'disabled-by-default-devtools.timeline', + 'disabled-by-default-devtools.timeline.frame', + 'toplevel', + 'blink.console', + 'blink.user_timing', + 'latencyInfo', + 'disabled-by-default-devtools.timeline.stack', + 'disabled-by-default-v8.cpu_profiler', + 'disabled-by-default-v8.cpu_profiler.hires', + ], + }); + await loadPage(); + await getDuration(); + await page.waitForTimeout(1000); + await page.tracing.stop(); + await client.send('Emulation.setCPUThrottlingRate', { rate: 1 }); + + // calculate durations + const times = suite.times ?? 5; + const durations: number[] = []; + for (let i = 0; i < times; i++) { + await loadPage(); + const duration = await getDuration(); durations.push(duration); } @@ -112,6 +174,7 @@ describe('benchmark: mutation observer', () => { durations: durations.join(', '), }, ]); + console.log('profile: ', profilePath); }); } }); diff --git a/packages/rrweb/test/html/benchmark-dom-mutation-add-and-remove.html b/packages/rrweb/test/html/benchmark-dom-mutation-add-and-remove.html new file mode 100644 index 0000000000..49efad4400 --- /dev/null +++ b/packages/rrweb/test/html/benchmark-dom-mutation-add-and-remove.html @@ -0,0 +1,46 @@ + + + + diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 1d36fa5ac1..969f11ff5d 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -192,6 +192,23 @@ describe('record', function (this: ISuite) { assertSnapshot(ctx.events); }); + it('should record scroll position', async () => { + await ctx.page.evaluate(() => { + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + emit: ((window as unknown) as IWindow).emit, + }); + const p = document.createElement('p'); + p.innerText = 'testtesttesttesttesttesttesttesttesttest'; + p.setAttribute('style', 'overflow: auto; height: 1px; width: 1px;'); + document.body.appendChild(p); + p.scrollTop = 10; + p.scrollLeft = 10; + }); + await waitForRAF(ctx.page); + assertSnapshot(ctx.events); + }); + it('can add custom event', async () => { await ctx.page.evaluate(() => { const { record, addCustomEvent } = ((window as unknown) as IWindow).rrweb; diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 2eb3a6a881..89bc28453b 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -56,7 +56,11 @@ export const startServer = (defaultPort: number = 3030) => const sanitizePath = path .normalize(parsedUrl.pathname!) .replace(/^(\.\.[\/\\])+/, ''); - const pathname = path.join(__dirname, sanitizePath); + + let pathname = path.join(__dirname, sanitizePath); + if (/^\/rrweb.*\.js.*/.test(sanitizePath)) { + pathname = path.join(__dirname, `../dist`, sanitizePath); + } try { const data = fs.readFileSync(pathname); diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index 0dacfb8245..9491527c8e 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -10,7 +10,7 @@ export declare function patch(source: { }, name: string, replacement: (...args: any[]) => any): () => void; export declare function getWindowHeight(): number; export declare function getWindowWidth(): number; -export declare function isBlocked(node: Node | null, blockClass: blockClass): boolean; +export declare function isBlocked(node: Node | null, blockClass: blockClass, checkAncestors: boolean): boolean; export declare function isSerialized(n: Node, mirror: Mirror): boolean; export declare function isIgnored(n: Node, mirror: Mirror): boolean; export declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean; diff --git a/yarn.lock b/yarn.lock index 4a130a685d..5f85e0dff5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1560,6 +1560,21 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@microsoft/tsdoc-config@0.16.1": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz#4de11976c1202854c4618f364bf499b4be33e657" + integrity sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ== + dependencies: + "@microsoft/tsdoc" "0.14.1" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd" + integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -2562,7 +2577,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@~6.12.6: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -4534,6 +4549,14 @@ eslint-plugin-svelte3@^4.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3" integrity sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g== +eslint-plugin-tsdoc@^0.2.16: + version "0.2.16" + resolved "https://registry.yarnpkg.com/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.16.tgz#a3d31fb9c7955faa3c66a43dd43da7635f1c5e0d" + integrity sha512-F/RWMnyDQuGlg82vQEFHQtGyWi7++XJKdYNn0ulIbyMOFqYIjoJOUdE6olORxgwgLkpJxsCJpJbTHgxJ/ggfXw== + dependencies: + "@microsoft/tsdoc" "0.14.1" + "@microsoft/tsdoc-config" "0.16.1" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" @@ -5908,6 +5931,13 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" +is-core-module@^2.1.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== + dependencies: + has "^1.0.3" + is-core-module@^2.2.0: version "2.5.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz" @@ -7137,6 +7167,11 @@ jest@^27.5.1: import-local "^3.0.2" jest-cli "^27.5.1" +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= + joycon@^3.0.1: version "3.1.1" resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" From 0fb8a6bfa8e327ac6dfdba21bbdafda46201cba5 Mon Sep 17 00:00:00 2001 From: eliyabar <13337543+eliyabar@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:43:05 +0300 Subject: [PATCH 014/201] Update dive-into-event.md (#914) --- docs/recipes/dive-into-event.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/recipes/dive-into-event.md b/docs/recipes/dive-into-event.md index b10442615e..4df9152c19 100644 --- a/docs/recipes/dive-into-event.md +++ b/docs/recipes/dive-into-event.md @@ -67,4 +67,4 @@ source -> IncrementalSource.Font data -> fontData ``` -enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/master/src/types.ts#L64). +enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/master/packages/rrweb/typings/types.d.ts#L62). From 74f553afc3bf8f6b631175416eac2fb641083142 Mon Sep 17 00:00:00 2001 From: yz-yu Date: Mon, 20 Jun 2022 22:02:21 +0800 Subject: [PATCH 015/201] move browser-only rrdom features to the new rrdom package (#913) --- .vscode/rrweb-monorepo.code-workspace | 4 + package.json | 3 +- packages/rrdom-nodejs/.gitignore | 4 + .../.vscode/extensions.json | 0 .../.vscode/launch.json | 0 .../.vscode/settings.json | 0 packages/rrdom-nodejs/jest.config.js | 5 + packages/rrdom-nodejs/package.json | 55 +++ packages/rrdom-nodejs/rollup.config.js | 84 ++++ .../src/document-nodejs.ts | 8 +- packages/rrdom-nodejs/src/index.ts | 13 + .../{rrdom => rrdom-nodejs}/src/polyfill.ts | 0 .../test/document-nodejs.test.ts | 4 +- .../test/polyfill.test.ts | 0 packages/rrdom-nodejs/tsconfig.json | 20 + packages/rrdom/package.json | 42 +- packages/rrdom/rollup.config.js | 10 - packages/rrdom/src/diff.ts | 20 +- packages/rrdom/src/index.ts | 461 +++++++++++++++++- packages/rrdom/src/virtual-dom.ts | 450 ----------------- packages/rrdom/test/diff.test.ts | 2 +- packages/rrdom/test/virtual-dom.test.ts | 8 +- packages/rrdom/tsconfig.json | 2 +- packages/rrweb/jest.config.js | 1 - packages/rrweb/src/replay/index.ts | 19 +- packages/rrweb/src/types.ts | 2 +- packages/rrweb/src/utils.ts | 2 +- packages/rrweb/typings/replay/index.d.ts | 2 +- packages/rrweb/typings/types.d.ts | 2 +- packages/rrweb/typings/utils.d.ts | 2 +- yarn.lock | 36 +- 31 files changed, 707 insertions(+), 554 deletions(-) create mode 100644 packages/rrdom-nodejs/.gitignore rename packages/{rrdom => rrdom-nodejs}/.vscode/extensions.json (100%) rename packages/{rrdom => rrdom-nodejs}/.vscode/launch.json (100%) rename packages/{rrdom => rrdom-nodejs}/.vscode/settings.json (100%) create mode 100644 packages/rrdom-nodejs/jest.config.js create mode 100644 packages/rrdom-nodejs/package.json create mode 100644 packages/rrdom-nodejs/rollup.config.js rename packages/{rrdom => rrdom-nodejs}/src/document-nodejs.ts (98%) create mode 100644 packages/rrdom-nodejs/src/index.ts rename packages/{rrdom => rrdom-nodejs}/src/polyfill.ts (100%) rename packages/{rrdom => rrdom-nodejs}/test/document-nodejs.test.ts (99%) rename packages/{rrdom => rrdom-nodejs}/test/polyfill.test.ts (100%) create mode 100644 packages/rrdom-nodejs/tsconfig.json delete mode 100644 packages/rrdom/src/virtual-dom.ts diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 1d100ed367..896a62e5d9 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -8,6 +8,10 @@ "name": "rrdom (package)", "path": "../packages/rrdom" }, + { + "name": "rrdom-nodejs (package)", + "path": "../packages/rrdom-nodejs" + }, { "name": "rrweb (package)", "path": "../packages/rrweb" diff --git a/package.json b/package.json index 61c9b5227d..9e6b495424 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "packages/rrweb", "packages/rrweb-snapshot", "packages/rrweb-player", - "packages/rrdom" + "packages/rrdom", + "packages/rrdom-nodejs" ], "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.25.0", diff --git a/packages/rrdom-nodejs/.gitignore b/packages/rrdom-nodejs/.gitignore new file mode 100644 index 0000000000..fb77b328d4 --- /dev/null +++ b/packages/rrdom-nodejs/.gitignore @@ -0,0 +1,4 @@ +dist +es +lib +typings diff --git a/packages/rrdom/.vscode/extensions.json b/packages/rrdom-nodejs/.vscode/extensions.json similarity index 100% rename from packages/rrdom/.vscode/extensions.json rename to packages/rrdom-nodejs/.vscode/extensions.json diff --git a/packages/rrdom/.vscode/launch.json b/packages/rrdom-nodejs/.vscode/launch.json similarity index 100% rename from packages/rrdom/.vscode/launch.json rename to packages/rrdom-nodejs/.vscode/launch.json diff --git a/packages/rrdom/.vscode/settings.json b/packages/rrdom-nodejs/.vscode/settings.json similarity index 100% rename from packages/rrdom/.vscode/settings.json rename to packages/rrdom-nodejs/.vscode/settings.json diff --git a/packages/rrdom-nodejs/jest.config.js b/packages/rrdom-nodejs/jest.config.js new file mode 100644 index 0000000000..e86e13bab9 --- /dev/null +++ b/packages/rrdom-nodejs/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/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json new file mode 100644 index 0000000000..331a66cf23 --- /dev/null +++ b/packages/rrdom-nodejs/package.json @@ -0,0 +1,55 @@ +{ + "name": "rrdom-nodejs", + "version": "0.1.2", + "scripts": { + "dev": "rollup -c -w", + "bundle": "rollup --config", + "bundle:es-only": "cross-env ES_ONLY=true rollup --config", + "check-types": "tsc -noEmit", + "test": "jest", + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" + }, + "keywords": [ + "rrweb", + "rrdom-nodejs" + ], + "license": "MIT", + "main": "lib/rrdom-nodejs.js", + "module": "es/rrdom-nodejs.js", + "typings": "es", + "files": [ + "dist", + "lib", + "es", + "typings" + ], + "devDependencies": { + "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-node-resolve": "^13.0.4", + "@types/cssom": "^0.4.1", + "@types/cssstyle": "^2.2.1", + "@types/jest": "^27.4.1", + "@types/nwsapi": "^2.2.2", + "@types/puppeteer": "^5.4.4", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", + "compare-versions": "^4.1.3", + "eslint": "^8.15.0", + "jest": "^27.5.1", + "puppeteer": "^9.1.1", + "rollup": "^2.56.3", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-web-worker-loader": "^1.6.1", + "ts-jest": "^27.1.3", + "typescript": "^4.6.2" + }, + "dependencies": { + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "nwsapi": "^2.2.0", + "rrweb-snapshot": "^1.1.14", + "rrdom": "^0.1.2" + } +} diff --git a/packages/rrdom-nodejs/rollup.config.js b/packages/rrdom-nodejs/rollup.config.js new file mode 100644 index 0000000000..233b0f1f3f --- /dev/null +++ b/packages/rrdom-nodejs/rollup.config.js @@ -0,0 +1,84 @@ +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import { terser } from 'rollup-plugin-terser'; +import typescript from 'rollup-plugin-typescript2'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; +import pkg from './package.json'; + +function toMinPath(path) { + return path.replace(/\.js$/, '.min.js'); +} + +const basePlugins = [ + resolve({ browser: true }), + commonjs(), + + // supports bundling `web-worker:..filename` from rrweb + webWorkerLoader(), + + typescript({ + tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, + }), +]; + +const baseConfigs = [ + { + input: './src/index.ts', + name: pkg.name, + path: pkg.name, + }, + { + input: './src/document-nodejs.ts', + name: 'RRDocument', + path: 'document-nodejs', + }, +]; + +let configs = []; +let extraConfigs = []; +for (let config of baseConfigs) { + configs.push( + // ES module + { + input: config.input, + plugins: basePlugins, + output: [ + { + format: 'esm', + file: pkg.module.replace(pkg.name, config.path), + }, + ], + }, + ); + extraConfigs.push( + // CommonJS + { + input: config.input, + plugins: basePlugins, + output: [ + { + format: 'cjs', + file: pkg.main.replace(pkg.name, config.path), + }, + ], + }, + // ES module (packed) + { + input: config.input, + plugins: basePlugins.concat(terser()), + output: [ + { + format: 'esm', + file: toMinPath(pkg.module).replace(pkg.name, config.path), + sourcemap: true, + }, + ], + }, + ); +} + +if (!process.env.ES_ONLY) { + configs.push(...extraConfigs); +} + +export default configs; diff --git a/packages/rrdom/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts similarity index 98% rename from packages/rrdom/src/document-nodejs.ts rename to packages/rrdom-nodejs/src/document-nodejs.ts index 3eb504fcca..249457227c 100644 --- a/packages/rrdom/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -13,7 +13,7 @@ import { ClassList, IRRDocument, CSSStyleDeclaration, -} from './document'; +} from 'rrdom'; const nwsapi = require('nwsapi'); const cssom = require('cssom'); const cssstyle = require('cssstyle'); @@ -53,22 +53,27 @@ export class RRDocument return this._nwsapi; } + // @ts-ignore get documentElement(): RRElement | null { return super.documentElement as RRElement | null; } + // @ts-ignore get body(): RRElement | null { return super.body as RRElement | null; } + // @ts-ignore get head() { return super.head as RRElement | null; } + // @ts-ignore get implementation(): RRDocument { return this; } + // @ts-ignore get firstElementChild(): RRElement | null { return this.documentElement; } @@ -198,6 +203,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) { }); } + // @ts-ignore get style() { return (this._style as unknown) as CSSStyleDeclaration; } diff --git a/packages/rrdom-nodejs/src/index.ts b/packages/rrdom-nodejs/src/index.ts new file mode 100644 index 0000000000..ac14bf73a4 --- /dev/null +++ b/packages/rrdom-nodejs/src/index.ts @@ -0,0 +1,13 @@ +import { + polyfillPerformance, + polyfillRAF, + polyfillEvent, + polyfillNode, + polyfillDocument, +} from './polyfill'; +polyfillPerformance(); +polyfillRAF(); +polyfillEvent(); +polyfillNode(); +polyfillDocument(); +export * from './document-nodejs'; diff --git a/packages/rrdom/src/polyfill.ts b/packages/rrdom-nodejs/src/polyfill.ts similarity index 100% rename from packages/rrdom/src/polyfill.ts rename to packages/rrdom-nodejs/src/polyfill.ts diff --git a/packages/rrdom/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts similarity index 99% rename from packages/rrdom/test/document-nodejs.test.ts rename to packages/rrdom-nodejs/test/document-nodejs.test.ts index f3b3c7b95b..ba3c6144d8 100644 --- a/packages/rrdom/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -16,7 +16,7 @@ import { RRStyleElement, RRText, } from '../src/document-nodejs'; -import { buildFromDom } from '../src/virtual-dom'; +import { buildFromDom } from 'rrdom'; describe('RRDocument for nodejs environment', () => { describe('RRDocument API', () => { @@ -542,6 +542,6 @@ describe('RRDocument for nodejs environment', () => { }); function getHtml(fileName: string) { - const filePath = path.resolve(__dirname, `./html/${fileName}`); + const filePath = path.resolve(__dirname, `../../rrdom/test/html/${fileName}`); return fs.readFileSync(filePath, 'utf8'); } diff --git a/packages/rrdom/test/polyfill.test.ts b/packages/rrdom-nodejs/test/polyfill.test.ts similarity index 100% rename from packages/rrdom/test/polyfill.test.ts rename to packages/rrdom-nodejs/test/polyfill.test.ts diff --git a/packages/rrdom-nodejs/tsconfig.json b/packages/rrdom-nodejs/tsconfig.json new file mode 100644 index 0000000000..4a4f18a080 --- /dev/null +++ b/packages/rrdom-nodejs/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs", + "noImplicitAny": true, + "strictNullChecks": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "build", + "lib": ["es6", "dom"], + "skipLibCheck": true, + "declaration": true, + "importsNotUsedAsValues": "error" + }, + "compileOnSave": true, + "exclude": ["test"], + "include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"] +} diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 3acef09350..6bc1e1a490 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,19 +1,7 @@ { "name": "rrdom", "version": "0.1.2", - "scripts": { - "dev": "rollup -c -w", - "bundle": "rollup --config", - "bundle:es-only": "cross-env ES_ONLY=true rollup --config", - "check-types": "tsc -noEmit", - "test": "jest", - "prepublish": "npm run bundle", - "lint": "yarn eslint src/**/*.ts" - }, - "keywords": [ - "rrweb", - "rrdom" - ], + "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", "module": "es/rrdom.js", @@ -25,17 +13,28 @@ "es", "typings" ], + "repository": { + "type": "git", + "url": "git+https://github.com/rrweb-io/rrweb.git" + }, + "scripts": { + "dev": "rollup -c -w", + "bundle": "rollup --config", + "bundle:es-only": "cross-env ES_ONLY=true rollup --config", + "check-types": "tsc -noEmit", + "test": "jest", + "prepublish": "npm run bundle", + "lint": "yarn eslint src/**/*.ts" + }, + "bugs": { + "url": "https://github.com/rrweb-io/rrweb/issues" + }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rollup/plugin-node-resolve": "^13.0.4", - "@types/cssom": "^0.4.1", - "@types/cssstyle": "^2.2.1", "@types/jest": "^27.4.1", - "@types/nwsapi": "^2.2.2", - "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", - "compare-versions": "^4.1.3", + "@types/puppeteer": "^5.4.4", "eslint": "^8.15.0", "jest": "^27.5.1", "puppeteer": "^9.1.1", @@ -43,13 +42,10 @@ "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", - "rrweb-snapshot": "^1.1.14", "ts-jest": "^27.1.3", "typescript": "^4.6.2" }, "dependencies": { - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "nwsapi": "^2.2.0" + "rrweb-snapshot": "^1.1.14" } } diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index fe1d74a15b..5bd346673f 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -27,16 +27,6 @@ const baseConfigs = [ name: pkg.name, path: pkg.name, }, - { - input: './src/document-nodejs.ts', - name: 'RRDocument', - path: 'document-nodejs', - }, - { - input: './src/virtual-dom.ts', - name: 'RRDocument', - path: 'virtual-dom', - }, ]; let configs = []; diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 8c493bcb7b..73850d6453 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -22,7 +22,7 @@ import type { RRStyleElement, RRDocument, Mirror, -} from './virtual-dom'; +} from '.'; const NAMESPACES: Record = { svg: 'http://www.w3.org/2000/svg', @@ -113,7 +113,7 @@ export function diff( break; } case RRNodeType.Element: { - const oldElement = (oldTree ) as HTMLElement; + const oldElement = oldTree as HTMLElement; const newRRElement = newTree as IRRElement; diffProps(oldElement, newRRElement, rrnodeMirror); scrollDataToApply = (newRRElement as RRElement).scrollData; @@ -121,7 +121,7 @@ export function diff( switch (newRRElement.tagName) { case 'AUDIO': case 'VIDEO': { - const oldMediaElement = (oldTree ) as HTMLMediaElement; + const oldMediaElement = oldTree as HTMLMediaElement; const newMediaRRElement = newRRElement as RRMediaElement; if (newMediaRRElement.paused !== undefined) newMediaRRElement.paused @@ -141,7 +141,7 @@ export function diff( replayer.applyCanvas( canvasMutation.event, canvasMutation.mutation, - (oldTree ) as HTMLCanvasElement, + oldTree as HTMLCanvasElement, ), ); break; @@ -191,8 +191,7 @@ export function diff( // IFrame element doesn't have child nodes. if (newTree.nodeName === 'IFRAME') { - const oldContentDocument = ((oldTree ) as HTMLIFrameElement) - .contentDocument; + const oldContentDocument = (oldTree as HTMLIFrameElement).contentDocument; const newIFrameElement = newTree as RRIFrameElement; // If the iframe is cross-origin, the contentDocument will be null. if (oldContentDocument) { @@ -319,11 +318,9 @@ function diffChildren( if ( replayer.mirror.getMeta(parentNode)?.type === RRNodeType.Document && replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element && - ((parentNode ) as Document).documentElement + (parentNode as Document).documentElement ) { - parentNode.removeChild( - ((parentNode ) as Document).documentElement, - ); + parentNode.removeChild((parentNode as Document).documentElement); oldChildren[oldStartIndex] = undefined; oldStartNode = undefined; } @@ -417,8 +414,7 @@ export function getNestedRule( return rule; } else { return getNestedRule( - ((rule ).cssRules[position[1]] as CSSGroupingRule) - .cssRules, + (rule.cssRules[position[1]] as CSSGroupingRule).cssRules, position.slice(2), ); } diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index ac14bf73a4..c81f6af698 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -1,13 +1,450 @@ import { - polyfillPerformance, - polyfillRAF, - polyfillEvent, - polyfillNode, - polyfillDocument, -} from './polyfill'; -polyfillPerformance(); -polyfillRAF(); -polyfillEvent(); -polyfillNode(); -polyfillDocument(); -export * from './document-nodejs'; + NodeType as RRNodeType, + createMirror as createNodeMirror, +} from 'rrweb-snapshot'; +import type { + Mirror as NodeMirror, + IMirror, + serializedNodeWithId, +} from 'rrweb-snapshot'; +import type { + canvasMutationData, + canvasEventWithTime, + inputData, + scrollData, +} from 'rrweb/src/types'; +import type { VirtualStyleRules } from './diff'; +import { + BaseRRNode as RRNode, + BaseRRCDATASectionImpl, + BaseRRCommentImpl, + BaseRRDocumentImpl, + BaseRRDocumentTypeImpl, + BaseRRElementImpl, + BaseRRMediaElementImpl, + BaseRRTextImpl, + IRRDocument, + IRRElement, + IRRNode, + NodeType, + IRRDocumentType, + IRRText, + IRRComment, +} from './document'; + +export class RRDocument extends BaseRRDocumentImpl(RRNode) { + // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. + // These unserialized nodes may interfere the execution of the diff algorithm. + // The id of serialized node is larger than 0. So this value ​​less than 0 is used as id for these unserialized nodes. + private _unserializedId = -1; + + /** + * Every time the id is used, it will minus 1 automatically to avoid collisions. + */ + public get unserializedId(): number { + return this._unserializedId--; + } + + public mirror: Mirror = createMirror(); + + public scrollData: scrollData | null = null; + + constructor(mirror?: Mirror) { + super(); + if (mirror) { + this.mirror = mirror; + } + } + + createDocument( + _namespace: string | null, + _qualifiedName: string | null, + _doctype?: DocumentType | null, + ) { + return new RRDocument(); + } + + createDocumentType( + qualifiedName: string, + publicId: string, + systemId: string, + ) { + const documentTypeNode = new RRDocumentType( + qualifiedName, + publicId, + systemId, + ); + documentTypeNode.ownerDocument = this; + return documentTypeNode; + } + + createElement( + tagName: K, + ): RRElementType; + createElement(tagName: string): RRElement; + createElement(tagName: string) { + const upperTagName = tagName.toUpperCase(); + let element; + switch (upperTagName) { + case 'AUDIO': + case 'VIDEO': + element = new RRMediaElement(upperTagName); + break; + case 'IFRAME': + element = new RRIFrameElement(upperTagName, this.mirror); + break; + case 'CANVAS': + element = new RRCanvasElement(upperTagName); + break; + case 'STYLE': + element = new RRStyleElement(upperTagName); + break; + default: + element = new RRElement(upperTagName); + break; + } + element.ownerDocument = this; + return element; + } + + createComment(data: string) { + const commentNode = new RRComment(data); + commentNode.ownerDocument = this; + return commentNode; + } + + createCDATASection(data: string) { + const sectionNode = new RRCDATASection(data); + sectionNode.ownerDocument = this; + return sectionNode; + } + + createTextNode(data: string) { + const textNode = new RRText(data); + textNode.ownerDocument = this; + return textNode; + } + + destroyTree() { + this.childNodes = []; + this.mirror.reset(); + } + + open() { + super.open(); + this._unserializedId = -1; + } +} + +export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); + +export class RRElement extends BaseRRElementImpl(RRNode) { + inputData: inputData | null = null; + scrollData: scrollData | null = null; +} + +export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} + +export class RRCanvasElement extends RRElement implements IRRElement { + public canvasMutations: { + event: canvasEventWithTime; + mutation: canvasMutationData; + }[] = []; + /** + * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. + */ + getContext(): RenderingContext | null { + return null; + } +} + +export class RRStyleElement extends RRElement { + public rules: VirtualStyleRules = []; +} + +export class RRIFrameElement extends RRElement { + contentDocument: RRDocument = new RRDocument(); + constructor(upperTagName: string, mirror: Mirror) { + super(upperTagName); + this.contentDocument.mirror = mirror; + } +} + +export const RRText = BaseRRTextImpl(RRNode); +export type RRText = typeof RRText; + +export const RRComment = BaseRRCommentImpl(RRNode); +export type RRComment = typeof RRComment; + +export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); +export type RRCDATASection = typeof RRCDATASection; + +interface RRElementTagNameMap { + audio: RRMediaElement; + canvas: RRCanvasElement; + iframe: RRIFrameElement; + style: RRStyleElement; + video: RRMediaElement; +} + +type RRElementType< + K extends keyof HTMLElementTagNameMap +> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; + +function getValidTagName(element: HTMLElement): string { + // https://github.com/rrweb-io/rrweb-snapshot/issues/56 + if (element instanceof HTMLFormElement) { + return 'FORM'; + } + return element.tagName.toUpperCase(); +} + +/** + * Build a RRNode from a real Node. + * @param node the real Node + * @param rrdom the RRDocument + * @param domMirror the NodeMirror that records the real document tree + * @returns the built RRNode + */ +export function buildFromNode( + node: Node, + rrdom: IRRDocument, + domMirror: NodeMirror, + parentRRNode?: IRRNode | null, +): IRRNode | null { + let rrNode: IRRNode; + + switch (node.nodeType) { + case NodeType.DOCUMENT_NODE: + if (parentRRNode && parentRRNode.nodeName === 'IFRAME') + rrNode = (parentRRNode as RRIFrameElement).contentDocument; + else { + rrNode = rrdom; + (rrNode as IRRDocument).compatMode = (node as Document).compatMode as + | 'BackCompat' + | 'CSS1Compat'; + } + break; + case NodeType.DOCUMENT_TYPE_NODE: + const documentType = node as DocumentType; + rrNode = rrdom.createDocumentType( + documentType.name, + documentType.publicId, + documentType.systemId, + ); + break; + case NodeType.ELEMENT_NODE: + const elementNode = node as HTMLElement; + const tagName = getValidTagName(elementNode); + rrNode = rrdom.createElement(tagName); + const rrElement = rrNode as IRRElement; + for (const { name, value } of Array.from(elementNode.attributes)) { + rrElement.attributes[name] = value; + } + elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); + elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); + /** + * We don't have to record special values of input elements at the beginning. + * Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed. + */ + break; + case NodeType.TEXT_NODE: + rrNode = rrdom.createTextNode((node as Text).textContent || ''); + break; + case NodeType.CDATA_SECTION_NODE: + rrNode = rrdom.createCDATASection((node as CDATASection).data); + break; + case NodeType.COMMENT_NODE: + rrNode = rrdom.createComment((node as Comment).textContent || ''); + break; + // if node is a shadow root + case NodeType.DOCUMENT_FRAGMENT_NODE: + rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' }); + break; + default: + return null; + } + + let sn: serializedNodeWithId | null = domMirror.getMeta(node); + + if (rrdom instanceof RRDocument) { + if (!sn) { + sn = getDefaultSN(rrNode, rrdom.unserializedId); + domMirror.add(node, sn); + } + rrdom.mirror.add(rrNode, { ...sn }); + } + + return rrNode; +} + +/** + * Build a RRDocument from a real document tree. + * @param dom the real document tree + * @param domMirror the NodeMirror that records the real document tree + * @param rrdom the rrdom object to be constructed + * @returns the build rrdom + */ +export function buildFromDom( + dom: Document, + domMirror: NodeMirror = createNodeMirror(), + rrdom: IRRDocument = new RRDocument(), +) { + function walk(node: Node, parentRRNode: IRRNode | null) { + const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); + if (rrNode === null) return; + if ( + // if the parentRRNode isn't a RRIFrameElement + parentRRNode?.nodeName !== 'IFRAME' && + // if node isn't a shadow root + node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE + ) { + parentRRNode?.appendChild(rrNode); + rrNode.parentNode = parentRRNode; + rrNode.parentElement = parentRRNode as RRElement; + } + + if (node.nodeName === 'IFRAME') { + walk((node as HTMLIFrameElement).contentDocument!, rrNode); + } else if ( + node.nodeType === NodeType.DOCUMENT_NODE || + node.nodeType === NodeType.ELEMENT_NODE || + node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE + ) { + // if the node is a shadow dom + if ( + node.nodeType === NodeType.ELEMENT_NODE && + (node as HTMLElement).shadowRoot + ) + walk((node as HTMLElement).shadowRoot!, rrNode); + node.childNodes.forEach((childNode) => walk(childNode, rrNode)); + } + } + walk(dom, null); + return rrdom; +} + +export function createMirror(): Mirror { + return new Mirror(); +} + +// based on Mirror from rrweb-snapshots +export class Mirror implements IMirror { + private idNodeMap: Map = new Map(); + private nodeMetaMap: WeakMap = new WeakMap(); + + getId(n: RRNode | undefined | null): number { + if (!n) return -1; + + const id = this.getMeta(n)?.id; + + // if n is not a serialized Node, use -1 as its id. + return id ?? -1; + } + + getNode(id: number): RRNode | null { + return this.idNodeMap.get(id) || null; + } + + getIds(): number[] { + return Array.from(this.idNodeMap.keys()); + } + + getMeta(n: RRNode): serializedNodeWithId | null { + return this.nodeMetaMap.get(n) || null; + } + + // removes the node from idNodeMap + // doesn't remove the node from nodeMetaMap + removeNodeFromMap(n: RRNode) { + const id = this.getId(n); + this.idNodeMap.delete(id); + + if (n.childNodes) { + n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); + } + } + has(id: number): boolean { + return this.idNodeMap.has(id); + } + + hasNode(node: RRNode): boolean { + return this.nodeMetaMap.has(node); + } + + add(n: RRNode, meta: serializedNodeWithId) { + const id = meta.id; + this.idNodeMap.set(id, n); + this.nodeMetaMap.set(n, meta); + } + + replace(id: number, n: RRNode) { + this.idNodeMap.set(id, n); + } + + reset() { + this.idNodeMap = new Map(); + this.nodeMetaMap = new WeakMap(); + } +} + +/** + * Get a default serializedNodeWithId value for a RRNode. + * @param id the serialized id to assign + */ +export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { + switch (node.RRNodeType) { + case RRNodeType.Document: + return { + id, + type: node.RRNodeType, + childNodes: [], + }; + case RRNodeType.DocumentType: + const doctype = node as IRRDocumentType; + return { + id, + type: node.RRNodeType, + name: doctype.name, + publicId: doctype.publicId, + systemId: doctype.systemId, + }; + case RRNodeType.Element: + return { + id, + type: node.RRNodeType, + tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. + attributes: {}, + childNodes: [], + }; + case RRNodeType.Text: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRText).textContent || '', + }; + case RRNodeType.Comment: + return { + id, + type: node.RRNodeType, + textContent: (node as IRRComment).textContent || '', + }; + case RRNodeType.CDATA: + return { + id, + type: node.RRNodeType, + textContent: '', + }; + } +} + +export { RRNode }; + +export { + diff, + createOrGetNode, + StyleRuleType, + ReplayerHandler, + VirtualStyleRules, +} from './diff'; +export * from './document'; diff --git a/packages/rrdom/src/virtual-dom.ts b/packages/rrdom/src/virtual-dom.ts deleted file mode 100644 index 78f1dad05e..0000000000 --- a/packages/rrdom/src/virtual-dom.ts +++ /dev/null @@ -1,450 +0,0 @@ -import { - NodeType as RRNodeType, - createMirror as createNodeMirror, -} from 'rrweb-snapshot'; -import type { - Mirror as NodeMirror, - IMirror, - serializedNodeWithId, -} from 'rrweb-snapshot'; -import type { - canvasMutationData, - canvasEventWithTime, - inputData, - scrollData, -} from 'rrweb/src/types'; -import { - BaseRRNode as RRNode, - BaseRRCDATASectionImpl, - BaseRRCommentImpl, - BaseRRDocumentImpl, - BaseRRDocumentTypeImpl, - BaseRRElementImpl, - BaseRRMediaElementImpl, - BaseRRTextImpl, - IRRDocument, - IRRElement, - IRRNode, - NodeType, - IRRDocumentType, - IRRText, - IRRComment, -} from './document'; -import type { VirtualStyleRules } from './diff'; - -export class RRDocument extends BaseRRDocumentImpl(RRNode) { - // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. - // These unserialized nodes may interfere the execution of the diff algorithm. - // The id of serialized node is larger than 0. So this value ​​less than 0 is used as id for these unserialized nodes. - private _unserializedId = -1; - - /** - * Every time the id is used, it will minus 1 automatically to avoid collisions. - */ - public get unserializedId(): number { - return this._unserializedId--; - } - - public mirror: Mirror = createMirror(); - - public scrollData: scrollData | null = null; - - constructor(mirror?: Mirror) { - super(); - if (mirror) { - this.mirror = mirror; - } - } - - createDocument( - _namespace: string | null, - _qualifiedName: string | null, - _doctype?: DocumentType | null, - ) { - return new RRDocument(); - } - - createDocumentType( - qualifiedName: string, - publicId: string, - systemId: string, - ) { - const documentTypeNode = new RRDocumentType( - qualifiedName, - publicId, - systemId, - ); - documentTypeNode.ownerDocument = this; - return documentTypeNode; - } - - createElement( - tagName: K, - ): RRElementType; - createElement(tagName: string): RRElement; - createElement(tagName: string) { - const upperTagName = tagName.toUpperCase(); - let element; - switch (upperTagName) { - case 'AUDIO': - case 'VIDEO': - element = new RRMediaElement(upperTagName); - break; - case 'IFRAME': - element = new RRIFrameElement(upperTagName, this.mirror); - break; - case 'CANVAS': - element = new RRCanvasElement(upperTagName); - break; - case 'STYLE': - element = new RRStyleElement(upperTagName); - break; - default: - element = new RRElement(upperTagName); - break; - } - element.ownerDocument = this; - return element; - } - - createComment(data: string) { - const commentNode = new RRComment(data); - commentNode.ownerDocument = this; - return commentNode; - } - - createCDATASection(data: string) { - const sectionNode = new RRCDATASection(data); - sectionNode.ownerDocument = this; - return sectionNode; - } - - createTextNode(data: string) { - const textNode = new RRText(data); - textNode.ownerDocument = this; - return textNode; - } - - destroyTree() { - this.childNodes = []; - this.mirror.reset(); - } - - open() { - super.open(); - this._unserializedId = -1; - } -} - -export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode); - -export class RRElement extends BaseRRElementImpl(RRNode) { - inputData: inputData | null = null; - scrollData: scrollData | null = null; -} - -export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} - -export class RRCanvasElement extends RRElement implements IRRElement { - public canvasMutations: { - event: canvasEventWithTime; - mutation: canvasMutationData; - }[] = []; - /** - * This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement. - */ - getContext(): RenderingContext | null { - return null; - } -} - -export class RRStyleElement extends RRElement { - public rules: VirtualStyleRules = []; -} - -export class RRIFrameElement extends RRElement { - contentDocument: RRDocument = new RRDocument(); - constructor(upperTagName: string, mirror: Mirror) { - super(upperTagName); - this.contentDocument.mirror = mirror; - } -} - -export const RRText = BaseRRTextImpl(RRNode); -export type RRText = typeof RRText; - -export const RRComment = BaseRRCommentImpl(RRNode); -export type RRComment = typeof RRComment; - -export const RRCDATASection = BaseRRCDATASectionImpl(RRNode); -export type RRCDATASection = typeof RRCDATASection; - -interface RRElementTagNameMap { - audio: RRMediaElement; - canvas: RRCanvasElement; - iframe: RRIFrameElement; - style: RRStyleElement; - video: RRMediaElement; -} - -type RRElementType< - K extends keyof HTMLElementTagNameMap -> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; - -function getValidTagName(element: HTMLElement): string { - // https://github.com/rrweb-io/rrweb-snapshot/issues/56 - if (element instanceof HTMLFormElement) { - return 'FORM'; - } - return element.tagName.toUpperCase(); -} - -/** - * Build a RRNode from a real Node. - * @param node the real Node - * @param rrdom the RRDocument - * @param domMirror the NodeMirror that records the real document tree - * @returns the built RRNode - */ -export function buildFromNode( - node: Node, - rrdom: IRRDocument, - domMirror: NodeMirror, - parentRRNode?: IRRNode | null, -): IRRNode | null { - let rrNode: IRRNode; - - switch (node.nodeType) { - case NodeType.DOCUMENT_NODE: - if (parentRRNode && parentRRNode.nodeName === 'IFRAME') - rrNode = (parentRRNode as RRIFrameElement).contentDocument; - else { - rrNode = rrdom; - (rrNode as IRRDocument).compatMode = (node as Document).compatMode as - | 'BackCompat' - | 'CSS1Compat'; - } - break; - case NodeType.DOCUMENT_TYPE_NODE: - const documentType = (node ) as DocumentType; - rrNode = rrdom.createDocumentType( - documentType.name, - documentType.publicId, - documentType.systemId, - ); - break; - case NodeType.ELEMENT_NODE: - const elementNode = (node ) as HTMLElement; - const tagName = getValidTagName(elementNode); - rrNode = rrdom.createElement(tagName); - const rrElement = rrNode as IRRElement; - for (const { name, value } of Array.from(elementNode.attributes)) { - rrElement.attributes[name] = value; - } - elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft); - elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop); - /** - * We don't have to record special values of input elements at the beginning. - * Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed. - */ - break; - case NodeType.TEXT_NODE: - rrNode = rrdom.createTextNode(((node ) as Text).textContent || ''); - break; - case NodeType.CDATA_SECTION_NODE: - rrNode = rrdom.createCDATASection(((node ) as CDATASection).data); - break; - case NodeType.COMMENT_NODE: - rrNode = rrdom.createComment( - ((node ) as Comment).textContent || '', - ); - break; - // if node is a shadow root - case NodeType.DOCUMENT_FRAGMENT_NODE: - rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' }); - break; - default: - return null; - } - - let sn: serializedNodeWithId | null = domMirror.getMeta(node); - - if (rrdom instanceof RRDocument) { - if (!sn) { - sn = getDefaultSN(rrNode, rrdom.unserializedId); - domMirror.add(node, sn); - } - rrdom.mirror.add(rrNode, { ...sn }); - } - - return rrNode; -} - -/** - * Build a RRDocument from a real document tree. - * @param dom the real document tree - * @param domMirror the NodeMirror that records the real document tree - * @param rrdom the rrdom object to be constructed - * @returns the build rrdom - */ -export function buildFromDom( - dom: Document, - domMirror: NodeMirror = createNodeMirror(), - rrdom: IRRDocument = new RRDocument(), -) { - function walk(node: Node, parentRRNode: IRRNode | null) { - const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode); - if (rrNode === null) return; - if ( - // if the parentRRNode isn't a RRIFrameElement - parentRRNode?.nodeName !== 'IFRAME' && - // if node isn't a shadow root - node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE - ) { - parentRRNode?.appendChild(rrNode); - rrNode.parentNode = parentRRNode; - rrNode.parentElement = parentRRNode as RRElement; - } - - if (node.nodeName === 'IFRAME') { - walk((node as HTMLIFrameElement).contentDocument!, rrNode); - } else if ( - node.nodeType === NodeType.DOCUMENT_NODE || - node.nodeType === NodeType.ELEMENT_NODE || - node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE - ) { - // if the node is a shadow dom - if ( - node.nodeType === NodeType.ELEMENT_NODE && - ((node ) as HTMLElement).shadowRoot - ) - walk(((node ) as HTMLElement).shadowRoot!, rrNode); - node.childNodes.forEach((childNode) => walk(childNode, rrNode)); - } - } - walk(dom, null); - return rrdom; -} - -export function createMirror(): Mirror { - return new Mirror(); -} - -// based on Mirror from rrweb-snapshots -export class Mirror implements IMirror { - private idNodeMap: Map = new Map(); - private nodeMetaMap: WeakMap = new WeakMap(); - - getId(n: RRNode | undefined | null): number { - if (!n) return -1; - - const id = this.getMeta(n)?.id; - - // if n is not a serialized Node, use -1 as its id. - return id ?? -1; - } - - getNode(id: number): RRNode | null { - return this.idNodeMap.get(id) || null; - } - - getIds(): number[] { - return Array.from(this.idNodeMap.keys()); - } - - getMeta(n: RRNode): serializedNodeWithId | null { - return this.nodeMetaMap.get(n) || null; - } - - // removes the node from idNodeMap - // doesn't remove the node from nodeMetaMap - removeNodeFromMap(n: RRNode) { - const id = this.getId(n); - this.idNodeMap.delete(id); - - if (n.childNodes) { - n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode)); - } - } - has(id: number): boolean { - return this.idNodeMap.has(id); - } - - hasNode(node: RRNode): boolean { - return this.nodeMetaMap.has(node); - } - - add(n: RRNode, meta: serializedNodeWithId) { - const id = meta.id; - this.idNodeMap.set(id, n); - this.nodeMetaMap.set(n, meta); - } - - replace(id: number, n: RRNode) { - this.idNodeMap.set(id, n); - } - - reset() { - this.idNodeMap = new Map(); - this.nodeMetaMap = new WeakMap(); - } -} - -/** - * Get a default serializedNodeWithId value for a RRNode. - * @param id the serialized id to assign - */ -export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { - switch (node.RRNodeType) { - case RRNodeType.Document: - return { - id, - type: node.RRNodeType, - childNodes: [], - }; - case RRNodeType.DocumentType: - const doctype = node as IRRDocumentType; - return { - id, - type: node.RRNodeType, - name: doctype.name, - publicId: doctype.publicId, - systemId: doctype.systemId, - }; - case RRNodeType.Element: - return { - id, - type: node.RRNodeType, - tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase. - attributes: {}, - childNodes: [], - }; - case RRNodeType.Text: - return { - id, - type: node.RRNodeType, - textContent: (node as IRRText).textContent || '', - }; - case RRNodeType.Comment: - return { - id, - type: node.RRNodeType, - textContent: (node as IRRComment).textContent || '', - }; - case RRNodeType.CDATA: - return { - id, - type: node.RRNodeType, - textContent: '', - }; - } -} - -export { RRNode }; -export { - diff, - createOrGetNode, - StyleRuleType, - VirtualStyleRules, - ReplayerHandler, -} from './diff'; diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 22feace950..bb4544e888 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -1,7 +1,7 @@ /** * @jest-environment jsdom */ -import { getDefaultSN, RRDocument, RRMediaElement } from '../src/virtual-dom'; +import { getDefaultSN, RRDocument, RRMediaElement } from '../src'; import { applyVirtualStyleRulesToNode, createOrGetNode, diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index 4bded073b2..b99a34325e 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -27,8 +27,8 @@ import { RRCanvasElement, RRDocument, RRElement, - RRNode, -} from '../src/virtual-dom'; + BaseRRNode as RRNode, +} from '../src'; const _typescript = (typescript as unknown) as typeof typescript.default; const printRRDomCode = ` @@ -219,9 +219,9 @@ describe('RRDocument for browser environment', () => { beforeAll(async () => { browser = await puppeteer.launch(); const bundle = await rollup.rollup({ - input: path.resolve(__dirname, '../src/virtual-dom.ts'), + input: path.resolve(__dirname, '../src/index.ts'), plugins: [ - resolve(), + (resolve() as unknown) as rollup.Plugin, (_typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, }) as unknown) as rollup.Plugin, diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 4a4f18a080..c5d366adf8 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -16,5 +16,5 @@ }, "compileOnSave": true, "exclude": ["test"], - "include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"] + "include": ["src", "../rrweb/src/record/workers/workers.d.ts"] } diff --git a/packages/rrweb/jest.config.js b/packages/rrweb/jest.config.js index c1f271c9b7..29db4e7fa0 100644 --- a/packages/rrweb/jest.config.js +++ b/packages/rrweb/jest.config.js @@ -5,6 +5,5 @@ module.exports = { testMatch: ['**/**.test.ts'], moduleNameMapper: { '\\.css$': 'identity-obj-proxy', - 'rrdom/es/(.*)': 'rrdom/lib/$1', }, }; diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index e83b82c2e7..99d64b99ca 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -15,7 +15,7 @@ import { buildFromDom, diff, getDefaultSN, -} from 'rrdom/es/virtual-dom'; +} from 'rrdom'; import type { RRNode, RRElement, @@ -26,7 +26,7 @@ import type { ReplayerHandler, Mirror as RRDOMMirror, VirtualStyleRules, -} from 'rrdom/es/virtual-dom'; +} from 'rrdom'; import * as mittProxy from 'mitt'; import { polyfill as smoothscrollPolyfill } from './smoothscroll'; import { Timer } from './timer'; @@ -731,7 +731,7 @@ export class Replayer { ); } if (this.usingVirtualDom) { - const styleEl = this.virtualDom.createElement('style') ; + const styleEl = this.virtualDom.createElement('style'); this.virtualDom.mirror.add( styleEl, getDefaultSN(styleEl, this.virtualDom.unserializedId), @@ -752,10 +752,7 @@ export class Replayer { head as HTMLHeadElement, ); for (let idx = 0; idx < injectStylesRules.length; idx++) { - (styleEl.sheet! ).insertRule( - injectStylesRules[idx], - idx, - ); + styleEl.sheet!.insertRule(injectStylesRules[idx], idx); } } } @@ -1210,7 +1207,7 @@ export class Replayer { if (!target) { return this.debugNodeNotFound(d, d.id); } - const styleSheet = ((target ) as HTMLStyleElement).sheet!; + const styleSheet = (target as HTMLStyleElement).sheet!; d.adds?.forEach(({ rule, index: nestedIndex }) => { try { if (Array.isArray(nestedIndex)) { @@ -1692,7 +1689,7 @@ export class Replayer { } } } else if (attributeName === 'style') { - const styleValues = value ; + const styleValues = value; const targetEl = target as HTMLElement | RRElement; for (const s in styleValues) { if (styleValues[s] === false) { @@ -1772,7 +1769,7 @@ export class Replayer { const previousInMap = previousId && map[previousId]; const nextInMap = nextId && map[nextId]; if (previousInMap) { - const { node, mutation } = previousInMap ; + const { node, mutation } = previousInMap; parent.insertBefore(node as Node & RRNode, target as Node & RRNode); delete map[mutation.node.id]; delete this.legacy_missingNodeRetryMap[mutation.node.id]; @@ -1781,7 +1778,7 @@ export class Replayer { } } if (nextInMap) { - const { node, mutation } = nextInMap ; + const { node, mutation } = nextInMap; parent.insertBefore( node as Node & RRNode, target.nextSibling as Node & RRNode, diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index a74a091c75..63a10943dd 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -11,7 +11,7 @@ import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import type { RRNode } from 'rrdom/es/virtual-dom'; +import type { RRNode } from 'rrdom'; import type { CanvasManager } from './record/observers/canvas/canvas-manager'; export enum EventType { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index ea8cd1f1b8..81108b58b5 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -11,7 +11,7 @@ import type { } from './types'; import type { IMirror, Mirror } from 'rrweb-snapshot'; import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot'; -import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom'; +import type { RRNode, RRIFrameElement } from 'rrdom'; export function on( type: string, diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts index 46b84e58c4..68dbc5bc8c 100644 --- a/packages/rrweb/typings/replay/index.d.ts +++ b/packages/rrweb/typings/replay/index.d.ts @@ -1,5 +1,5 @@ import { Mirror } from 'rrweb-snapshot'; -import { RRDocument } from 'rrdom/es/virtual-dom'; +import { RRDocument } from 'rrdom'; import { Timer } from './timer'; import { createPlayerService, createSpeedService } from './machine'; import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types'; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts index 61f3d8b84c..50a7109a75 100644 --- a/packages/rrweb/typings/types.d.ts +++ b/packages/rrweb/typings/types.d.ts @@ -3,7 +3,7 @@ import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; import type { ShadowDomManager } from './record/shadow-dom-manager'; import type { Replayer } from './replay'; -import type { RRNode } from 'rrdom/es/virtual-dom'; +import type { RRNode } from 'rrdom'; import type { CanvasManager } from './record/observers/canvas/canvas-manager'; export declare enum EventType { DomContentLoaded = 0, diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts index 9491527c8e..0fabf6f199 100644 --- a/packages/rrweb/typings/utils.d.ts +++ b/packages/rrweb/typings/utils.d.ts @@ -1,6 +1,6 @@ import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types'; import type { IMirror, Mirror } from 'rrweb-snapshot'; -import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom'; +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; diff --git a/yarn.lock b/yarn.lock index 5f85e0dff5..9f82432e29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1791,10 +1791,10 @@ magic-string "^0.25.7" resolve "^1.17.0" -"@rollup/plugin-node-resolve@^13.0.4": - version "13.0.6" - resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz" - integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA== +"@rollup/plugin-node-resolve@^13.0.4", "@rollup/plugin-node-resolve@^13.2.1": + version "13.2.1" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz" + integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== dependencies: "@rollup/pluginutils" "^3.1.0" "@types/resolve" "1.17.1" @@ -1815,18 +1815,6 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-node-resolve@^13.2.1": - version "13.2.1" - resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz" - integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA== - dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - builtin-modules "^3.1.0" - deepmerge "^4.2.2" - is-module "^1.0.0" - resolve "^1.19.0" - "@rollup/plugin-typescript@^8.2.5": version "8.2.5" resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz" @@ -1965,12 +1953,12 @@ "@types/cssom@^0.4.1": version "0.4.1" - resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz" + resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz#fb64e145b425bd6c1b0ed78ebd66ba43b6e088ab" integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ== "@types/cssstyle@^2.2.1": version "2.2.1" - resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz" + resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz#fa010824006ff47af94a6b9baf9759e031815347" integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ== "@types/estree@*": @@ -2088,7 +2076,7 @@ "@types/nwsapi@^2.2.2": version "2.2.2" - resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz" + resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz#1b1dccfc38b2b7e1b9ea71d5285796878375e862" integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g== "@types/offscreencanvas@^2019.6.4": @@ -3499,7 +3487,7 @@ compare-func@^2.0.0: compare-versions@^4.1.3: version "4.1.3" - resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== concat-map@0.0.1: @@ -9676,6 +9664,14 @@ 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.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" From d35110521ac5ca30b04b9159655692f4edc29c32 Mon Sep 17 00:00:00 2001 From: yz-yu Date: Mon, 20 Jun 2022 22:55:10 +0800 Subject: [PATCH 016/201] integrate turborepo in monorepo (#918) * integrate turborepo in monorepo * integrate turborepo in monorepo --- .gitignore | 2 ++ package.json | 15 ++++------ turbo.json | 19 ++++++++++++ yarn.lock | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 turbo.json diff --git a/.gitignore b/.gitignore index 895e1115aa..808676098e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ temp build dist + +.turbo diff --git a/package.json b/package.json index 9e6b495424..3d5b0e336a 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,7 @@ "private": true, "homepage": "https://github.com/rrweb-io/rrweb#readme", "workspaces": [ - "packages/rrweb", - "packages/rrweb-snapshot", - "packages/rrweb-player", - "packages/rrdom", - "packages/rrdom-nodejs" + "packages/*" ], "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.25.0", @@ -30,14 +26,15 @@ "lerna": "^4.0.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", + "turbo": "^1.2.4", "typescript": "^4.6.4" }, "scripts": { "lerna": "lerna", - "build:all": "yarn lerna run prepublish", - "test": "yarn lerna run test", - "test:watch": "yarn lerna run test:watch --parallel", - "dev": "yarn lerna run dev --parallel", + "build:all": "yarn turbo run prepublish", + "test": "yarn turbo run test", + "test:watch": "yarn turbo run test:watch", + "dev": "yarn turbo run dev", "repl": "cd packages/rrweb && npm run repl", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'" }, diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000000..5d1a58238b --- /dev/null +++ b/turbo.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "baseBranch": "origin/master", + "pipeline": { + "prepublish": { + "dependsOn": [ + "^prepublish" + ], + "outputs": [ + "lib/**", + "es/**", + "dist/**" + ] + }, + "test": {}, + "test:watch": {}, + "dev": {} + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9f82432e29..f6c36883b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10808,6 +10808,90 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +turbo-darwin-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.2.16.tgz#1586fd6e52ffb5ad5e1e3ec8afe1c873f7fd98d9" + integrity sha512-dyitLQJdH3uLVdlH9jAkP4LqEO/K+wOXjUqOzjTciRLjQPzmsNY60/bmFHODADK4eBBl1nxbtn7tmmoT4vS1qA== + +turbo-darwin-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.2.16.tgz#3faf9c657fa5feb16465316ac8d47c4a2790c2f1" + integrity sha512-Ex6uM4HU7rGXdhvJMpzNpp6qxglJ98nWeIi5qR/lBXHLjK3UCvSW8BEALArUJYJTXS9FZBq1a5LowFqXYsfDcA== + +turbo-freebsd-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-freebsd-64/-/turbo-freebsd-64-1.2.16.tgz#2674c2078eaa79200a3b91639e530b287a735cb4" + integrity sha512-onRGKMvog8B3XDssSBIAg+FrEq9pcBoAybP7bpi/uYIH1L/WQ7YMmLn88X9JX19ehYuVOVZrjap4jWH2GIkU8A== + +turbo-freebsd-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-freebsd-arm64/-/turbo-freebsd-arm64-1.2.16.tgz#88cc25733b19880b4a14a9d4f34770da9e898a92" + integrity sha512-S0EqPqxwnJuVNNXRgcHB0r8ai8LSrpHdihVJKRM7WYmIR7isccBEf/G9agrt73sCXwjvenxFs4HDR7cSvGt14Q== + +turbo-linux-32@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-32/-/turbo-linux-32-1.2.16.tgz#7c60a19acd32862085566da0f9389d2db36a7aee" + integrity sha512-ecbqmGOxgTWePGrowtwyvZGfvwaLxFWmPK21cU0PS+fzoZBaVmzYmniTdd/2EkGCw7TOPhtiT22v96fWcnRycA== + +turbo-linux-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.2.16.tgz#d589bf73bbfa38fa73ad8ec786c0b89b678e7af8" + integrity sha512-q6gtdMWCzM0Sktkd73zcaQjNoeM1MjtrbwQBctWN/Sgj0eiPBPnzpIvokvx98x7RLf4qyI99/mlme0Dn5fx21A== + +turbo-linux-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.2.16.tgz#9eaaee4094f71a266553248dfaadc59756696242" + integrity sha512-gUf67tYJ/N09WAZTTmtUWYrqm381tZxiulnRGAIM+iRsaTrweyUKZaYXwJvlPpI/cQOw25wCG9/IyvxLeagL8A== + +turbo-linux-arm@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-arm/-/turbo-linux-arm-1.2.16.tgz#54774c7e6e4f91ebdb211fa48b1b2a46b1e3c61c" + integrity sha512-du7uvExELNb89V3g7iM0XP21fR1Yl3EoHRcOfQz32oUqnS7idCKvbEowM9LtiluQl1dKcOIJjn1nlvvsqzkhOg== + +turbo-linux-mips64le@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-mips64le/-/turbo-linux-mips64le-1.2.16.tgz#fa49b858f8ed399b44cdce285a0feb67c30ee99b" + integrity sha512-U5BM+Ql3z13uRtwMmKH/8eL+9DdTgyijC2gaX4xP0RTlcN7WfAstg8Fg/Tn2Vw9vtpVDdxwpw7dvX4kw2ghhpA== + +turbo-linux-ppc64le@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-linux-ppc64le/-/turbo-linux-ppc64le-1.2.16.tgz#0a419310b938ef5eace46cb6c53b18099caae60c" + integrity sha512-HQWSCmVZyc5chw7Ie2ZcfZPfmM06mbEEu0Wl11Y5QWh1ZzhPNQHs/TsF4I9r146wHi62XgcrKFjkw4ARZiWsLA== + +turbo-windows-32@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-32/-/turbo-windows-32-1.2.16.tgz#8307dea3fab91be4e3e46aa37d76ce8e7ce3fdcb" + integrity sha512-0ZtPz5FK2qZjznMG4vvRyaabrhO8BgbN+tBx1wjXSuoICTAjYi5TwRVVRh59c3x7qQmR21Cv33CrhLBPRfeAlg== + +turbo-windows-64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.2.16.tgz#9f5f725d6ab829d3cf7944ba92714e9e5bdd09d0" + integrity sha512-j8iAIixq/rGfBpHNbYOosxMasZrGuMzLILEuQGDxZgKNpYgobJ15QFHQlGR9sit1b8qPU5zZX4CtByRtkgH1Bw== + +turbo-windows-arm64@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.2.16.tgz#75894186e9f1d6340a30c436d563c34999e5b87c" + integrity sha512-4GpcJG3B8R9WDhwfT8fu6ZmOOfseCg6Q1cy/G8/zpJQk769yYcSnD8MgQhYgHB58aVFxZcMxBvLL6UA0UrpgWA== + +turbo@^1.2.4: + version "1.2.16" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.2.16.tgz#324c2d586f6fa77aa9173274f3b9d9efbe0735e4" + integrity sha512-PPUa2COKgFkyb6N3uF9AnIY3l9FZkF15QQ3U1K2wpI01D3gyGKQO0Q3DUQ4ipmciP0teBfL7H+l/QTrUA9IVvQ== + optionalDependencies: + turbo-darwin-64 "1.2.16" + turbo-darwin-arm64 "1.2.16" + turbo-freebsd-64 "1.2.16" + turbo-freebsd-arm64 "1.2.16" + turbo-linux-32 "1.2.16" + turbo-linux-64 "1.2.16" + turbo-linux-arm "1.2.16" + turbo-linux-arm64 "1.2.16" + turbo-linux-mips64le "1.2.16" + turbo-linux-ppc64le "1.2.16" + turbo-windows-32 "1.2.16" + turbo-windows-64 "1.2.16" + turbo-windows-arm64 "1.2.16" + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" From af8ed5513c359b7cda786659514a27bc1346448a Mon Sep 17 00:00:00 2001 From: yz-yu Date: Sat, 25 Jun 2022 21:08:06 +0800 Subject: [PATCH 017/201] unify typescript version and rollup plugins (#921) --- package.json | 2 +- packages/rrdom-nodejs/package.json | 2 +- packages/rrdom/package.json | 2 +- packages/rrweb-player/package.json | 4 +- packages/rrweb-player/rollup.config.js | 3 +- packages/rrweb-snapshot/package.json | 4 +- packages/rrweb-snapshot/rollup.config.js | 2 +- .../rrweb-snapshot/test/integration.test.ts | 4 +- packages/rrweb/package.json | 2 +- yarn.lock | 39 ++----------------- 10 files changed, 17 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index 3d5b0e336a..cddbf73f0a 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", "turbo": "^1.2.4", - "typescript": "^4.6.4" + "typescript": "^4.7.3" }, "scripts": { "lerna": "lerna", diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 331a66cf23..9c9c890349 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -43,7 +43,7 @@ "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", - "typescript": "^4.6.2" + "typescript": "^4.7.3" }, "dependencies": { "cssom": "^0.5.0", diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 6bc1e1a490..c80ee641b5 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -43,7 +43,7 @@ "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", - "typescript": "^4.6.2" + "typescript": "^4.7.3" }, "dependencies": { "rrweb-snapshot": "^1.1.14" diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index e005b6e191..fb94f95d53 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -4,7 +4,7 @@ "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rollup/plugin-typescript": "^8.3.2", + "rollup-plugin-typescript2": "^0.31.2", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -20,7 +20,7 @@ "svelte-check": "^1.4.0", "svelte-preprocess": "^4.0.0", "tslib": "^2.0.0", - "typescript": "^4.6.4" + "typescript": "^4.7.3" }, "dependencies": { "@tsconfig/svelte": "^1.0.0", diff --git a/packages/rrweb-player/rollup.config.js b/packages/rrweb-player/rollup.config.js index 403bf883d2..67aebc8052 100644 --- a/packages/rrweb-player/rollup.config.js +++ b/packages/rrweb-player/rollup.config.js @@ -5,7 +5,7 @@ import livereload from 'rollup-plugin-livereload'; import { terser } from 'rollup-plugin-terser'; import sveltePreprocess from 'svelte-preprocess'; import webWorkerLoader from 'rollup-plugin-web-worker-loader'; -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import pkg from './package.json'; import css from 'rollup-plugin-css-only'; @@ -64,6 +64,7 @@ export default entries.map((output) => ({ resolve({ browser: true, dedupe: ['svelte'], + extensions: ['.js', '.ts', '.svelte'], }), commonjs(), diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 7f0465198f..4da6741fea 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -40,7 +40,7 @@ }, "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-snapshot#readme", "devDependencies": { - "@rollup/plugin-typescript": "^8.2.5", + "rollup-plugin-typescript2": "^0.31.2", "@types/chai": "^4.1.4", "@types/jest": "^27.0.2", "@types/jsdom": "^16.2.4", @@ -56,6 +56,6 @@ "ts-jest": "^27.0.5", "ts-node": "^7.0.1", "tslib": "^1.9.3", - "typescript": "^3.9.7" + "typescript": "^4.7.3" } } diff --git a/packages/rrweb-snapshot/rollup.config.js b/packages/rrweb-snapshot/rollup.config.js index 6a8b0a0933..b13f447864 100644 --- a/packages/rrweb-snapshot/rollup.config.js +++ b/packages/rrweb-snapshot/rollup.config.js @@ -1,4 +1,4 @@ -import typescript from '@rollup/plugin-typescript'; +import typescript from 'rollup-plugin-typescript2'; import { terser } from 'rollup-plugin-terser'; import pkg from './package.json'; diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index c34bfefcc1..e34432fd7b 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -4,10 +4,10 @@ import * as http from 'http'; import * as url from 'url'; import * as puppeteer from 'puppeteer'; import * as rollup from 'rollup'; -import * as typescript from '@rollup/plugin-typescript'; +import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; -const _typescript = (typescript as unknown) as typeof typescript.default; +const _typescript = (typescript as unknown) as () => rollup.Plugin; const htmlFolder = path.join(__dirname, 'html'); const htmls = fs.readdirSync(htmlFolder).map((filePath) => { diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 62da2925bc..c51df94833 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -71,7 +71,7 @@ "ts-jest": "^27.1.3", "ts-node": "^10.7.0", "tslib": "^2.3.1", - "typescript": "^4.6.2" + "typescript": "^4.7.3" }, "dependencies": { "@types/css-font-loading-module": "0.0.7", diff --git a/yarn.lock b/yarn.lock index f6c36883b2..60d5042dd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1815,22 +1815,6 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-typescript@^8.2.5": - version "8.2.5" - resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz" - integrity sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - -"@rollup/plugin-typescript@^8.3.2": - version "8.3.2" - resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz" - integrity sha512-MtgyR5LNHZr3GyN0tM7gNO9D0CS+Y+vflS4v/PHmrX17JCkHUYKvQ5jN5o3cz1YKllM3duXUqu3yOHwMPUxhDg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - "@rollup/pluginutils@4": version "4.1.1" resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.1.tgz" @@ -10971,25 +10955,10 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@*: - version "4.3.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== - -typescript@^3.9.7: - version "3.9.10" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== - -typescript@^4.6.2: - version "4.6.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz" - integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== - -typescript@^4.6.4: - version "4.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +typescript@*, typescript@^4.7.3: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From c81e609d7b322221110f65de8b4deb9b60253958 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 30 Jun 2022 17:20:26 +0200 Subject: [PATCH 018/201] Lock yarn to 1.23.0 (#922) * Lock yarn to 1.23.0 * fix flaky test * Fix flaky tests --- .gitattributes | 2 + .gitignore | 5 +- .yarn/releases/yarn-1.23.0-20220130.1630.cjs | 147392 ++++++++++++++++ .yarnrc.yml | 1 + packages/rrweb/test/integration.test.ts | 59 +- 5 files changed, 147430 insertions(+), 29 deletions(-) create mode 100644 .gitattributes create mode 100755 .yarn/releases/yarn-1.23.0-20220130.1630.cjs create mode 100644 .yarnrc.yml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..40b8ea9dc8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/.yarn/releases/** binary +/.yarn/plugins/** binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 808676098e..af311e6796 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .idea node_modules package-lock.json -# yarn.lock temp @@ -17,3 +16,7 @@ build dist .turbo + +# `.yarn/install-state.gz` is an optimization file that you shouldn't ever have to commit. +# It simply stores the exact state of your project so that the next commands can boot without having to resolve your workspaces all over again. +.yarn/install-state.gz \ No newline at end of file diff --git a/.yarn/releases/yarn-1.23.0-20220130.1630.cjs b/.yarn/releases/yarn-1.23.0-20220130.1630.cjs new file mode 100755 index 0000000000..d3ff20a11a --- /dev/null +++ b/.yarn/releases/yarn-1.23.0-20220130.1630.cjs @@ -0,0 +1,147392 @@ +#!/usr/bin/env node +module.exports = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 549); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +module.exports = require("path"); + +/***/ }), +/* 1 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (immutable) */ __webpack_exports__["a"] = __extends; +/* unused harmony export __assign */ +/* unused harmony export __rest */ +/* unused harmony export __decorate */ +/* unused harmony export __param */ +/* unused harmony export __metadata */ +/* unused harmony export __awaiter */ +/* unused harmony export __generator */ +/* unused harmony export __exportStar */ +/* unused harmony export __values */ +/* unused harmony export __read */ +/* unused harmony export __spread */ +/* unused harmony export __await */ +/* unused harmony export __asyncGenerator */ +/* unused harmony export __asyncDelegator */ +/* unused harmony export __asyncValues */ +/* unused harmony export __makeTemplateObject */ +/* unused harmony export __importStar */ +/* unused harmony export __importDefault */ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* global Reflect, Promise */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + } + return __assign.apply(this, arguments); +} + +function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) + t[p[i]] = s[p[i]]; + return t; +} + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +} + +function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); +} + +function __awaiter(thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + +function __exportStar(m, exports) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} + +function __values(o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +} + +function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +} + +function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; +} + +function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); +} + +function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +} + +function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } +} + +function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +} + +function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; + +function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result.default = mod; + return result; +} + +function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; +} + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +exports.__esModule = true; + +var _promise = __webpack_require__(227); + +var _promise2 = _interopRequireDefault(_promise); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = function (fn) { + return function () { + var gen = fn.apply(this, arguments); + return new _promise2.default(function (resolve, reject) { + function step(key, arg) { + try { + var info = gen[key](arg); + var value = info.value; + } catch (error) { + reject(error); + return; + } + + if (info.done) { + resolve(value); + } else { + return _promise2.default.resolve(value).then(function (value) { + step("next", value); + }, function (err) { + step("throw", err); + }); + } + } + + return step("next"); + }); + }; +}; + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +module.exports = require("util"); + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; + +var _asyncToGenerator2; + +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); +} + +let buildActionsForCopy = (() => { + var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { + + // + let build = (() => { + var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest, + type = data.type; + + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; + + // TODO https://github.com/yarnpkg/yarn/issues/3751 + // related to bundled dependencies handling + if (files.has(dest.toLowerCase())) { + reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); + } else { + files.add(dest.toLowerCase()); + } + + if (type === 'symlink') { + yield mkdirp((_path || _load_path()).default.dirname(dest)); + onFresh(); + actions.symlink.push({ + dest, + linkname: src + }); + onDone(); + return; + } + + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; + } + + const srcStat = yield lstat(src); + let srcFiles; + + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); + } + + let destStat; + try { + // try accessing the destination + destStat = yield lstat(dest); + } catch (e) { + // proceed if destination doesn't exist, otherwise error + if (e.code !== 'ENOENT') { + throw e; + } + } + + // if destination exists + if (destStat) { + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); + + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. + + /* if (srcStat.mode !== destStat.mode) { + try { + await access(dest, srcStat.mode); + } catch (err) {} + } */ + + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } + + if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { + // we can safely assume this is the same file + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); + return; + } + + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } + + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); + + for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref6; + + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref6 = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref6 = _i4.value; + } + + const file = _ref6; + + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); + + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref7; + + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref7 = _iterator5[_i5++]; + } else { + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref7 = _i5.value; + } + + const file = _ref7; + + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } + } + + if (destStat && destStat.isSymbolicLink()) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); + destStat = null; + } + + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + if (!destStat) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); + } + + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } + + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref8; + + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref8 = _iterator6[_i6++]; + } else { + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref8 = _i6.value; + } + + const file = _ref8; + + queue.push({ + dest: (_path || _load_path()).default.join(dest, file), + onFresh, + onDone: function (_onDone) { + function onDone() { + return _onDone.apply(this, arguments); + } + + onDone.toString = function () { + return _onDone.toString(); + }; + + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }), + src: (_path || _load_path()).default.join(src, file) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.file.push({ + src, + dest, + atime: srcStat.atime, + mtime: srcStat.mtime, + mode: srcStat.mode + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); + + return function build(_x5) { + return _ref5.apply(this, arguments); + }; + })(); + + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); + + // initialise events + for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref2; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref2 = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref2 = _i.value; + } + + const item = _ref2; + + const onDone = item.onDone; + item.onDone = function () { + events.onProgress(item.dest); + if (onDone) { + onDone(); + } + }; + } + events.onStart(queue.length); + + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; + + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); + } + + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref3; + + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref3 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref3 = _i2.value; + } + + const file = _ref3; + + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); + } + } + + for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref4; + + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref4 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref4 = _i3.value; + } + + const loc = _ref4; + + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); + } + } + + return actions; + }); + + return function buildActionsForCopy(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); + }; +})(); + +let buildActionsForHardlink = (() => { + var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { + + // + let build = (() => { + var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest; + + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; + if (files.has(dest.toLowerCase())) { + // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 + // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, + // package-linker passes that modules A1 and B1 need to be hardlinked, + // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case + // an exception. + onDone(); + return; + } + files.add(dest.toLowerCase()); + + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; + } + + const srcStat = yield lstat(src); + let srcFiles; + + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); + } + + const destExists = yield exists(dest); + if (destExists) { + const destStat = yield lstat(dest); + + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); + + if (srcStat.mode !== destStat.mode) { + try { + yield access(dest, srcStat.mode); + } catch (err) { + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. + reporter.verbose(err); + } + } + + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } + + // correct hardlink + if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); + return; + } + + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } + + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); + + for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref14; + + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref14 = _iterator10[_i10++]; + } else { + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref14 = _i10.value; + } + + const file = _ref14; + + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); + + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { + var _ref15; + + if (_isArray11) { + if (_i11 >= _iterator11.length) break; + _ref15 = _iterator11[_i11++]; + } else { + _i11 = _iterator11.next(); + if (_i11.done) break; + _ref15 = _i11.value; + } + + const file = _ref15; + + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } + } + + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); + + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } + + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { + var _ref16; + + if (_isArray12) { + if (_i12 >= _iterator12.length) break; + _ref16 = _iterator12[_i12++]; + } else { + _i12 = _iterator12.next(); + if (_i12.done) break; + _ref16 = _i12.value; + } + + const file = _ref16; + + queue.push({ + onFresh, + src: (_path || _load_path()).default.join(src, file), + dest: (_path || _load_path()).default.join(dest, file), + onDone: function (_onDone2) { + function onDone() { + return _onDone2.apply(this, arguments); + } + + onDone.toString = function () { + return _onDone2.toString(); + }; + + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.link.push({ + src, + dest, + removeDest: destExists + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); + + return function build(_x10) { + return _ref13.apply(this, arguments); + }; + })(); + + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); + + // initialise events + for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + var _ref10; + + if (_isArray7) { + if (_i7 >= _iterator7.length) break; + _ref10 = _iterator7[_i7++]; + } else { + _i7 = _iterator7.next(); + if (_i7.done) break; + _ref10 = _i7.value; + } + + const item = _ref10; + + const onDone = item.onDone || noop; + item.onDone = function () { + events.onProgress(item.dest); + onDone(); + }; + } + events.onStart(queue.length); + + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; + + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); + } + + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref11; + + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref11 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref11 = _i8.value; + } + + const file = _ref11; + + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); + } + } + + for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref12; + + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref12 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref12 = _i9.value; + } + + const loc = _ref12; + + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); + } + } + + return actions; + }); + + return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { + return _ref9.apply(this, arguments); + }; +})(); + +let copyBulk = exports.copyBulk = (() => { + var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + ignoreBasenames: _events && _events.ignoreBasenames || [], + artifactFiles: _events && _events.artifactFiles || [] + }; + + const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); + + const fileActions = actions.file; + + const currentlyWriting = new Map(); + + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + let writePromise; + while (writePromise = currentlyWriting.get(data.dest)) { + yield writePromise; + } + + reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); + const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { + return currentlyWriting.delete(data.dest); + }); + currentlyWriting.set(data.dest, copier); + events.onProgress(data.dest); + return copier; + }); + + return function (_x14) { + return _ref18.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); + + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); + + return function copyBulk(_x11, _x12, _x13) { + return _ref17.apply(this, arguments); + }; +})(); + +let hardlinkBulk = exports.hardlinkBulk = (() => { + var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + artifactFiles: _events && _events.artifactFiles || [], + ignoreBasenames: [] + }; + + const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); + + const fileActions = actions.link; + + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); + if (data.removeDest) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); + } + yield link(data.src, data.dest); + }); + + return function (_x18) { + return _ref20.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); + + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); + + return function hardlinkBulk(_x15, _x16, _x17) { + return _ref19.apply(this, arguments); + }; +})(); + +let readFileAny = exports.readFileAny = (() => { + var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { + for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { + var _ref22; + + if (_isArray13) { + if (_i13 >= _iterator13.length) break; + _ref22 = _iterator13[_i13++]; + } else { + _i13 = _iterator13.next(); + if (_i13.done) break; + _ref22 = _i13.value; + } + + const file = _ref22; + + if (yield exists(file)) { + return readFile(file); + } + } + return null; + }); + + return function readFileAny(_x19) { + return _ref21.apply(this, arguments); + }; +})(); + +let readJson = exports.readJson = (() => { + var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + return (yield readJsonAndFile(loc)).object; + }); + + return function readJson(_x20) { + return _ref23.apply(this, arguments); + }; +})(); + +let readJsonAndFile = exports.readJsonAndFile = (() => { + var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const file = yield readFile(loc); + try { + return { + object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), + content: file + }; + } catch (err) { + err.message = `${loc}: ${err.message}`; + throw err; + } + }); + + return function readJsonAndFile(_x21) { + return _ref24.apply(this, arguments); + }; +})(); + +let find = exports.find = (() => { + var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { + const parts = dir.split((_path || _load_path()).default.sep); + + while (parts.length) { + const loc = parts.concat(filename).join((_path || _load_path()).default.sep); + + if (yield exists(loc)) { + return loc; + } else { + parts.pop(); + } + } + + return false; + }); + + return function find(_x22, _x23) { + return _ref25.apply(this, arguments); + }; +})(); + +let symlink = exports.symlink = (() => { + var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { + if (process.platform !== 'win32') { + // use relative paths otherwise which will be retained if the directory is moved + src = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); + // When path.relative returns an empty string for the current directory, we should instead use + // '.', which is a valid fs.symlink target. + src = src || '.'; + } + + try { + const stats = yield lstat(dest); + if (stats.isSymbolicLink()) { + const resolved = dest; + if (resolved === src) { + return; + } + } + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + + // We use rimraf for unlink which never throws an ENOENT on missing target + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); + + if (process.platform === 'win32') { + // use directory junctions if possible on win32, this requires absolute paths + yield fsSymlink(src, dest, 'junction'); + } else { + yield fsSymlink(src, dest); + } + }); + + return function symlink(_x24, _x25) { + return _ref26.apply(this, arguments); + }; +})(); + +let walk = exports.walk = (() => { + var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { + let files = []; + + let filenames = yield readdir(dir); + if (ignoreBasenames.size) { + filenames = filenames.filter(function (name) { + return !ignoreBasenames.has(name); + }); + } + + for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { + var _ref28; + + if (_isArray14) { + if (_i14 >= _iterator14.length) break; + _ref28 = _iterator14[_i14++]; + } else { + _i14 = _iterator14.next(); + if (_i14.done) break; + _ref28 = _i14.value; + } + + const name = _ref28; + + const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; + const loc = (_path || _load_path()).default.join(dir, name); + const stat = yield lstat(loc); + + files.push({ + relative, + basename: name, + absolute: loc, + mtime: +stat.mtime + }); + + if (stat.isDirectory()) { + files = files.concat((yield walk(loc, relative, ignoreBasenames))); + } + } + + return files; + }); + + return function walk(_x26, _x27) { + return _ref27.apply(this, arguments); + }; +})(); + +let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { + var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const stat = yield lstat(loc); + const size = stat.size, + blockSize = stat.blksize; + + + return Math.ceil(size / blockSize) * blockSize; + }); + + return function getFileSizeOnDisk(_x28) { + return _ref29.apply(this, arguments); + }; +})(); + +let getEolFromFile = (() => { + var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { + if (!(yield exists(path))) { + return undefined; + } + + const buffer = yield readFileBuffer(path); + + for (let i = 0; i < buffer.length; ++i) { + if (buffer[i] === cr) { + return '\r\n'; + } + if (buffer[i] === lf) { + return '\n'; + } + } + return undefined; + }); + + return function getEolFromFile(_x29) { + return _ref30.apply(this, arguments); + }; +})(); + +let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { + var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { + const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; + if (eol !== '\n') { + data = data.replace(/\n/g, eol); + } + yield writeFile(path, data); + }); + + return function writeFilePreservingEol(_x30, _x31) { + return _ref31.apply(this, arguments); + }; +})(); + +let hardlinksWork = exports.hardlinksWork = (() => { + var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { + const filename = 'test-file' + Math.random(); + const file = (_path || _load_path()).default.join(dir, filename); + const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); + try { + yield writeFile(file, 'test'); + yield link(file, fileLink); + } catch (err) { + return false; + } finally { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); + } + return true; + }); + + return function hardlinksWork(_x32) { + return _ref32.apply(this, arguments); + }; +})(); + +// not a strict polyfill for Node's fs.mkdtemp + + +let makeTempDir = exports.makeTempDir = (() => { + var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { + const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); + yield mkdirp(dir); + return dir; + }); + + return function makeTempDir(_x33) { + return _ref33.apply(this, arguments); + }; +})(); + +let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { + var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { + for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { + var _ref35; + + if (_isArray15) { + if (_i15 >= _iterator15.length) break; + _ref35 = _iterator15[_i15++]; + } else { + _i15 = _iterator15.next(); + if (_i15.done) break; + _ref35 = _i15.value; + } + + const path = _ref35; + + try { + const fd = yield open(path, 'r'); + return (_fs || _load_fs()).default.createReadStream(path, { fd }); + } catch (err) { + // Try the next one + } + } + return null; + }); + + return function readFirstAvailableStream(_x34) { + return _ref34.apply(this, arguments); + }; +})(); + +let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { + var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { + const result = { + skipped: [], + folder: null + }; + + for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { + var _ref37; + + if (_isArray16) { + if (_i16 >= _iterator16.length) break; + _ref37 = _iterator16[_i16++]; + } else { + _i16 = _iterator16.next(); + if (_i16.done) break; + _ref37 = _i16.value; + } + + const folder = _ref37; + + try { + yield mkdirp(folder); + yield access(folder, mode); + + result.folder = folder; + + return result; + } catch (error) { + result.skipped.push({ + error, + folder + }); + } + } + return result; + }); + + return function getFirstSuitableFolder(_x35) { + return _ref36.apply(this, arguments); + }; +})(); + +exports.copy = copy; +exports.readFile = readFile; +exports.readFileRaw = readFileRaw; +exports.normalizeOS = normalizeOS; + +var _fs; + +function _load_fs() { + return _fs = _interopRequireDefault(__webpack_require__(5)); +} + +var _glob; + +function _load_glob() { + return _glob = _interopRequireDefault(__webpack_require__(99)); +} + +var _os; + +function _load_os() { + return _os = _interopRequireDefault(__webpack_require__(46)); +} + +var _path; + +function _load_path() { + return _path = _interopRequireDefault(__webpack_require__(0)); +} + +var _blockingQueue; + +function _load_blockingQueue() { + return _blockingQueue = _interopRequireDefault(__webpack_require__(110)); +} + +var _promise; + +function _load_promise() { + return _promise = _interopRequireWildcard(__webpack_require__(50)); +} + +var _promise2; + +function _load_promise2() { + return _promise2 = __webpack_require__(50); +} + +var _map; + +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(29)); +} + +var _fsNormalized; + +function _load_fsNormalized() { + return _fsNormalized = __webpack_require__(218); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { + R_OK: (_fs || _load_fs()).default.R_OK, + W_OK: (_fs || _load_fs()).default.W_OK, + X_OK: (_fs || _load_fs()).default.X_OK +}; + +const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); + +const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); +const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); +const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); +const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); +const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); +const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); +const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); +const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); +const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); +const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(145)); +const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); +const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); +const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); +const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); +const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); +exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; + +// fs.copyFile uses the native file copying instructions on the system, performing much better +// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the +// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. + +const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; + +const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); +const invariant = __webpack_require__(9); +const stripBOM = __webpack_require__(160); + +const noop = () => {}; + +function copy(src, dest, reporter) { + return copyBulk([{ src, dest }], reporter); +} + +function _readFile(loc, encoding) { + return new Promise((resolve, reject) => { + (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { + if (err) { + reject(err); + } else { + resolve(content); + } + }); + }); +} + +function readFile(loc) { + return _readFile(loc, 'utf8').then(normalizeOS); +} + +function readFileRaw(loc) { + return _readFile(loc, 'binary'); +} + +function normalizeOS(body) { + return body.replace(/\r\n/g, '\n'); +} + +const cr = '\r'.charCodeAt(0); +const lf = '\n'.charCodeAt(0); + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +module.exports = require("fs"); + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +class MessageError extends Error { + constructor(msg, code) { + super(msg); + this.code = code; + } + +} + +exports.MessageError = MessageError; +class ProcessSpawnError extends MessageError { + constructor(msg, code, process) { + super(msg, code); + this.process = process; + } + +} + +exports.ProcessSpawnError = ProcessSpawnError; +class SecurityError extends MessageError {} + +exports.SecurityError = SecurityError; +class ProcessTermError extends MessageError {} + +exports.ProcessTermError = ProcessTermError; +class ResponseError extends Error { + constructor(msg, responseCode) { + super(msg); + this.responseCode = responseCode; + } + +} + +exports.ResponseError = ResponseError; +class OneTimePasswordError extends Error {} +exports.OneTimePasswordError = OneTimePasswordError; + +/***/ }), +/* 7 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscriber; }); +/* unused harmony export SafeSubscriber */ +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isFunction__ = __webpack_require__(154); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Observer__ = __webpack_require__(420); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__ = __webpack_require__(321); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__config__ = __webpack_require__(185); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__util_hostReportError__ = __webpack_require__(323); +/** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ + + + + + + + +var Subscriber = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subscriber, _super); + function Subscriber(destinationOrNext, error, complete) { + var _this = _super.call(this) || this; + _this.syncErrorValue = null; + _this.syncErrorThrown = false; + _this.syncErrorThrowable = false; + _this.isStopped = false; + _this._parentSubscription = null; + switch (arguments.length) { + case 0: + _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; + break; + case 1: + if (!destinationOrNext) { + _this.destination = __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]; + break; + } + if (typeof destinationOrNext === 'object') { + if (destinationOrNext instanceof Subscriber) { + _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; + _this.destination = destinationOrNext; + destinationOrNext.add(_this); + } + else { + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext); + } + break; + } + default: + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); + break; + } + return _this; + } + Subscriber.prototype[__WEBPACK_IMPORTED_MODULE_4__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { return this; }; + Subscriber.create = function (next, error, complete) { + var subscriber = new Subscriber(next, error, complete); + subscriber.syncErrorThrowable = false; + return subscriber; + }; + Subscriber.prototype.next = function (value) { + if (!this.isStopped) { + this._next(value); + } + }; + Subscriber.prototype.error = function (err) { + if (!this.isStopped) { + this.isStopped = true; + this._error(err); + } + }; + Subscriber.prototype.complete = function () { + if (!this.isStopped) { + this.isStopped = true; + this._complete(); + } + }; + Subscriber.prototype.unsubscribe = function () { + if (this.closed) { + return; + } + this.isStopped = true; + _super.prototype.unsubscribe.call(this); + }; + Subscriber.prototype._next = function (value) { + this.destination.next(value); + }; + Subscriber.prototype._error = function (err) { + this.destination.error(err); + this.unsubscribe(); + }; + Subscriber.prototype._complete = function () { + this.destination.complete(); + this.unsubscribe(); + }; + Subscriber.prototype._unsubscribeAndRecycle = function () { + var _a = this, _parent = _a._parent, _parents = _a._parents; + this._parent = null; + this._parents = null; + this.unsubscribe(); + this.closed = false; + this.isStopped = false; + this._parent = _parent; + this._parents = _parents; + this._parentSubscription = null; + return this; + }; + return Subscriber; +}(__WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */])); + +var SafeSubscriber = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SafeSubscriber, _super); + function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { + var _this = _super.call(this) || this; + _this._parentSubscriber = _parentSubscriber; + var next; + var context = _this; + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(observerOrNext)) { + next = observerOrNext; + } + else if (observerOrNext) { + next = observerOrNext.next; + error = observerOrNext.error; + complete = observerOrNext.complete; + if (observerOrNext !== __WEBPACK_IMPORTED_MODULE_2__Observer__["a" /* empty */]) { + context = Object.create(observerOrNext); + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isFunction__["a" /* isFunction */])(context.unsubscribe)) { + _this.add(context.unsubscribe.bind(context)); + } + context.unsubscribe = _this.unsubscribe.bind(_this); + } + } + _this._context = context; + _this._next = next; + _this._error = error; + _this._complete = complete; + return _this; + } + SafeSubscriber.prototype.next = function (value) { + if (!this.isStopped && this._next) { + var _parentSubscriber = this._parentSubscriber; + if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(this._next, value); + } + else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var _parentSubscriber = this._parentSubscriber; + var useDeprecatedSynchronousErrorHandling = __WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling; + if (this._error) { + if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(this._error, err); + this.unsubscribe(); + } + else { + this.__tryOrSetError(_parentSubscriber, this._error, err); + this.unsubscribe(); + } + } + else if (!_parentSubscriber.syncErrorThrowable) { + this.unsubscribe(); + if (useDeprecatedSynchronousErrorHandling) { + throw err; + } + __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); + } + else { + if (useDeprecatedSynchronousErrorHandling) { + _parentSubscriber.syncErrorValue = err; + _parentSubscriber.syncErrorThrown = true; + } + else { + __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); + } + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.complete = function () { + var _this = this; + if (!this.isStopped) { + var _parentSubscriber = this._parentSubscriber; + if (this._complete) { + var wrappedComplete = function () { return _this._complete.call(_this._context); }; + if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(wrappedComplete); + this.unsubscribe(); + } + else { + this.__tryOrSetError(_parentSubscriber, wrappedComplete); + this.unsubscribe(); + } + } + else { + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { + try { + fn.call(this._context, value); + } + catch (err) { + this.unsubscribe(); + if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { + throw err; + } + else { + __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); + } + } + }; + SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { + if (!__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { + throw new Error('bad call'); + } + try { + fn.call(this._context, value); + } + catch (err) { + if (__WEBPACK_IMPORTED_MODULE_5__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { + parent.syncErrorValue = err; + parent.syncErrorThrown = true; + return true; + } + else { + __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_6__util_hostReportError__["a" /* hostReportError */])(err); + return true; + } + } + return false; + }; + SafeSubscriber.prototype._unsubscribe = function () { + var _parentSubscriber = this._parentSubscriber; + this._context = null; + this._parentSubscriber = null; + _parentSubscriber.unsubscribe(); + }; + return SafeSubscriber; +}(Subscriber)); + +//# sourceMappingURL=Subscriber.js.map + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPathKey = getPathKey; +const os = __webpack_require__(46); +const path = __webpack_require__(0); +const userHome = __webpack_require__(67).default; + +var _require = __webpack_require__(225); + +const getCacheDir = _require.getCacheDir, + getConfigDir = _require.getConfigDir, + getDataDir = _require.getDataDir; + +const isWebpackBundle = __webpack_require__(278); + +const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; +const OWNED_DEPENDENCY_TYPES = exports.OWNED_DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies']; + +const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; +const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; + +const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; + +const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; +const NPM_REGISTRY_RE = exports.NPM_REGISTRY_RE = /https?:\/\/registry\.npmjs\.org/g; + +const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; +const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; +const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; + +const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; + +// cache version, bump whenever we make backwards incompatible changes +const CACHE_VERSION = exports.CACHE_VERSION = 6; + +// lockfile version, bump whenever we make backwards incompatible changes +const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; + +// max amount of network requests to perform concurrently +const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; + +// HTTP timeout used when downloading packages +const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds + +// max amount of child processes to execute concurrently +const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; + +const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; + +function getPreferredCacheDirectories() { + const preferredCacheDirectories = [getCacheDir()]; + + if (process.getuid) { + // $FlowFixMe: process.getuid exists, dammit + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); + } + + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); + + return preferredCacheDirectories; +} + +const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); +const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); +const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); +const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); +const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); + +const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; +const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); + +// Webpack needs to be configured with node.__dirname/__filename = false +function getYarnBinPath() { + if (isWebpackBundle) { + return __filename; + } else { + return path.join(__dirname, '..', 'bin', 'yarn.js'); + } +} + +const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; +const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; + +const PNP_FILENAME = exports.PNP_FILENAME = '.pnp.js'; + +const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; +const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); + +const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; +const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; +const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; +const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; +const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; +const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; + +const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; +const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; + +const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; +const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; +const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; + +const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); + +function getPathKey(platform, env) { + let pathKey = 'PATH'; + + // windows calls its path "Path" usually, but this is not guaranteed. + if (platform === 'win32') { + pathKey = 'Path'; + + for (const key in env) { + if (key.toLowerCase() === 'path') { + pathKey = key; + } + } + } + + return pathKey; +} + +const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { + major: 'red', + premajor: 'red', + minor: 'yellow', + preminor: 'yellow', + patch: 'green', + prepatch: 'green', + prerelease: 'red', + unchanged: 'white', + unknown: 'red' +}; + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + + +/** + * Use invariant() to assert state which your program assumes to be true. + * + * Provide sprintf-style format (only %s is supported) and arguments + * to provide information about what broke and what you were + * expecting. + * + * The invariant message will be stripped in production, but the invariant + * will remain to ensure logic does not differ in production. + */ + +var NODE_ENV = process.env.NODE_ENV; + +var invariant = function(condition, format, a, b, c, d, e, f) { + if (NODE_ENV !== 'production') { + if (format === undefined) { + throw new Error('invariant requires an error message argument'); + } + } + + if (!condition) { + var error; + if (format === undefined) { + error = new Error( + 'Minified exception occurred; use the non-minified dev environment ' + + 'for the full error message and additional helpful warnings.' + ); + } else { + var args = [a, b, c, d, e, f]; + var argIndex = 0; + error = new Error( + format.replace(/%s/g, function() { return args[argIndex++]; }) + ); + error.name = 'Invariant Violation'; + } + + error.framesToPop = 1; // we don't care about invariant's own frame + throw error; + } +}; + +module.exports = invariant; + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var YAMLException = __webpack_require__(54); + +var TYPE_CONSTRUCTOR_OPTIONS = [ + 'kind', + 'resolve', + 'construct', + 'instanceOf', + 'predicate', + 'represent', + 'defaultStyle', + 'styleAliases' +]; + +var YAML_NODE_KINDS = [ + 'scalar', + 'sequence', + 'mapping' +]; + +function compileStyleAliases(map) { + var result = {}; + + if (map !== null) { + Object.keys(map).forEach(function (style) { + map[style].forEach(function (alias) { + result[String(alias)] = style; + }); + }); + } + + return result; +} + +function Type(tag, options) { + options = options || {}; + + Object.keys(options).forEach(function (name) { + if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { + throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); + } + }); + + // TODO: Add tag format check. + this.tag = tag; + this.kind = options['kind'] || null; + this.resolve = options['resolve'] || function () { return true; }; + this.construct = options['construct'] || function (data) { return data; }; + this.instanceOf = options['instanceOf'] || null; + this.predicate = options['predicate'] || null; + this.represent = options['represent'] || null; + this.defaultStyle = options['defaultStyle'] || null; + this.styleAliases = compileStyleAliases(options['styleAliases'] || null); + + if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { + throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); + } +} + +module.exports = Type; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + +module.exports = require("crypto"); + +/***/ }), +/* 12 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Observable; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_canReportError__ = __webpack_require__(322); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__ = __webpack_require__(932); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__ = __webpack_require__(117); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_pipe__ = __webpack_require__(324); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__config__ = __webpack_require__(185); +/** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_internal_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ + + + + + +var Observable = /*@__PURE__*/ (function () { + function Observable(subscribe) { + this._isScalar = false; + if (subscribe) { + this._subscribe = subscribe; + } + } + Observable.prototype.lift = function (operator) { + var observable = new Observable(); + observable.source = this; + observable.operator = operator; + return observable; + }; + Observable.prototype.subscribe = function (observerOrNext, error, complete) { + var operator = this.operator; + var sink = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_toSubscriber__["a" /* toSubscriber */])(observerOrNext, error, complete); + if (operator) { + operator.call(sink, this.source); + } + else { + sink.add(this.source || (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? + this._subscribe(sink) : + this._trySubscribe(sink)); + } + if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { + if (sink.syncErrorThrowable) { + sink.syncErrorThrowable = false; + if (sink.syncErrorThrown) { + throw sink.syncErrorValue; + } + } + } + return sink; + }; + Observable.prototype._trySubscribe = function (sink) { + try { + return this._subscribe(sink); + } + catch (err) { + if (__WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].useDeprecatedSynchronousErrorHandling) { + sink.syncErrorThrown = true; + sink.syncErrorValue = err; + } + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_canReportError__["a" /* canReportError */])(sink)) { + sink.error(err); + } + else { + console.warn(err); + } + } + }; + Observable.prototype.forEach = function (next, promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function (resolve, reject) { + var subscription; + subscription = _this.subscribe(function (value) { + try { + next(value); + } + catch (err) { + reject(err); + if (subscription) { + subscription.unsubscribe(); + } + } + }, reject, resolve); + }); + }; + Observable.prototype._subscribe = function (subscriber) { + var source = this.source; + return source && source.subscribe(subscriber); + }; + Observable.prototype[__WEBPACK_IMPORTED_MODULE_2__internal_symbol_observable__["a" /* observable */]] = function () { + return this; + }; + Observable.prototype.pipe = function () { + var operations = []; + for (var _i = 0; _i < arguments.length; _i++) { + operations[_i] = arguments[_i]; + } + if (operations.length === 0) { + return this; + } + return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_pipe__["b" /* pipeFromArray */])(operations)(this); + }; + Observable.prototype.toPromise = function (promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function (resolve, reject) { + var value; + _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); }); + }); + }; + Observable.create = function (subscribe) { + return new Observable(subscribe); + }; + return Observable; +}()); + +function getPromiseCtor(promiseCtor) { + if (!promiseCtor) { + promiseCtor = __WEBPACK_IMPORTED_MODULE_4__config__["a" /* config */].Promise || Promise; + } + if (!promiseCtor) { + throw new Error('no Promise impl found'); + } + return promiseCtor; +} +//# sourceMappingURL=Observable.js.map + + +/***/ }), +/* 13 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return OuterSubscriber; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Subscriber__ = __webpack_require__(7); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + + +var OuterSubscriber = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](OuterSubscriber, _super); + function OuterSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + this.destination.next(innerValue); + }; + OuterSubscriber.prototype.notifyError = function (error, innerSub) { + this.destination.error(error); + }; + OuterSubscriber.prototype.notifyComplete = function (innerSub) { + this.destination.complete(); + }; + return OuterSubscriber; +}(__WEBPACK_IMPORTED_MODULE_1__Subscriber__["a" /* Subscriber */])); + +//# sourceMappingURL=OuterSubscriber.js.map + + +/***/ }), +/* 14 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (immutable) */ __webpack_exports__["a"] = subscribeToResult; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__ = __webpack_require__(84); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__subscribeTo__ = __webpack_require__(446); +/** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo PURE_IMPORTS_END */ + + +function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, destination) { + if (destination === void 0) { + destination = new __WEBPACK_IMPORTED_MODULE_0__InnerSubscriber__["a" /* InnerSubscriber */](outerSubscriber, outerValue, outerIndex); + } + if (destination.closed) { + return; + } + return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__subscribeTo__["a" /* subscribeTo */])(result)(destination); +} +//# sourceMappingURL=subscribeToResult.js.map + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* eslint-disable node/no-deprecated-api */ + + + +var buffer = __webpack_require__(64) +var Buffer = buffer.Buffer + +var safer = {} + +var key + +for (key in buffer) { + if (!buffer.hasOwnProperty(key)) continue + if (key === 'SlowBuffer' || key === 'Buffer') continue + safer[key] = buffer[key] +} + +var Safer = safer.Buffer = {} +for (key in Buffer) { + if (!Buffer.hasOwnProperty(key)) continue + if (key === 'allocUnsafe' || key === 'allocUnsafeSlow') continue + Safer[key] = Buffer[key] +} + +safer.Buffer.prototype = Buffer.prototype + +if (!Safer.from || Safer.from === Uint8Array.from) { + Safer.from = function (value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('The "value" argument must not be of type number. Received type ' + typeof value) + } + if (value && typeof value.length === 'undefined') { + throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ' + typeof value) + } + return Buffer(value, encodingOrOffset, length) + } +} + +if (!Safer.alloc) { + Safer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('The "size" argument must be of type number. Received type ' + typeof size) + } + if (size < 0 || size >= 2 * (1 << 30)) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } + var buf = Buffer(size) + if (!fill || fill.length === 0) { + buf.fill(0) + } else if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + return buf + } +} + +if (!safer.kStringMaxLength) { + try { + safer.kStringMaxLength = process.binding('buffer').kStringMaxLength + } catch (e) { + // we can't determine kStringMaxLength in environments where process.binding + // is unsupported, so let's not set it + } +} + +if (!safer.constants) { + safer.constants = { + MAX_LENGTH: safer.kMaxLength + } + if (safer.kStringMaxLength) { + safer.constants.MAX_STRING_LENGTH = safer.kStringMaxLength + } +} + +module.exports = safer + + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright (c) 2012, Mark Cavage. All rights reserved. +// Copyright 2015 Joyent, Inc. + +var assert = __webpack_require__(28); +var Stream = __webpack_require__(23).Stream; +var util = __webpack_require__(3); + + +///--- Globals + +/* JSSTYLED */ +var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; + + +///--- Internal + +function _capitalize(str) { + return (str.charAt(0).toUpperCase() + str.slice(1)); +} + +function _toss(name, expected, oper, arg, actual) { + throw new assert.AssertionError({ + message: util.format('%s (%s) is required', name, expected), + actual: (actual === undefined) ? typeof (arg) : actual(arg), + expected: expected, + operator: oper || '===', + stackStartFunction: _toss.caller + }); +} + +function _getClass(arg) { + return (Object.prototype.toString.call(arg).slice(8, -1)); +} + +function noop() { + // Why even bother with asserts? +} + + +///--- Exports + +var types = { + bool: { + check: function (arg) { return typeof (arg) === 'boolean'; } + }, + func: { + check: function (arg) { return typeof (arg) === 'function'; } + }, + string: { + check: function (arg) { return typeof (arg) === 'string'; } + }, + object: { + check: function (arg) { + return typeof (arg) === 'object' && arg !== null; + } + }, + number: { + check: function (arg) { + return typeof (arg) === 'number' && !isNaN(arg); + } + }, + finite: { + check: function (arg) { + return typeof (arg) === 'number' && !isNaN(arg) && isFinite(arg); + } + }, + buffer: { + check: function (arg) { return Buffer.isBuffer(arg); }, + operator: 'Buffer.isBuffer' + }, + array: { + check: function (arg) { return Array.isArray(arg); }, + operator: 'Array.isArray' + }, + stream: { + check: function (arg) { return arg instanceof Stream; }, + operator: 'instanceof', + actual: _getClass + }, + date: { + check: function (arg) { return arg instanceof Date; }, + operator: 'instanceof', + actual: _getClass + }, + regexp: { + check: function (arg) { return arg instanceof RegExp; }, + operator: 'instanceof', + actual: _getClass + }, + uuid: { + check: function (arg) { + return typeof (arg) === 'string' && UUID_REGEXP.test(arg); + }, + operator: 'isUUID' + } +}; + +function _setExports(ndebug) { + var keys = Object.keys(types); + var out; + + /* re-export standard assert */ + if (process.env.NODE_NDEBUG) { + out = noop; + } else { + out = function (arg, msg) { + if (!arg) { + _toss(msg, 'true', arg); + } + }; + } + + /* standard checks */ + keys.forEach(function (k) { + if (ndebug) { + out[k] = noop; + return; + } + var type = types[k]; + out[k] = function (arg, msg) { + if (!type.check(arg)) { + _toss(msg, k, type.operator, arg, type.actual); + } + }; + }); + + /* optional checks */ + keys.forEach(function (k) { + var name = 'optional' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + out[name] = function (arg, msg) { + if (arg === undefined || arg === null) { + return; + } + if (!type.check(arg)) { + _toss(msg, k, type.operator, arg, type.actual); + } + }; + }); + + /* arrayOf checks */ + keys.forEach(function (k) { + var name = 'arrayOf' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + var expected = '[' + k + ']'; + out[name] = function (arg, msg) { + if (!Array.isArray(arg)) { + _toss(msg, expected, type.operator, arg, type.actual); + } + var i; + for (i = 0; i < arg.length; i++) { + if (!type.check(arg[i])) { + _toss(msg, expected, type.operator, arg, type.actual); + } + } + }; + }); + + /* optionalArrayOf checks */ + keys.forEach(function (k) { + var name = 'optionalArrayOf' + _capitalize(k); + if (ndebug) { + out[name] = noop; + return; + } + var type = types[k]; + var expected = '[' + k + ']'; + out[name] = function (arg, msg) { + if (arg === undefined || arg === null) { + return; + } + if (!Array.isArray(arg)) { + _toss(msg, expected, type.operator, arg, type.actual); + } + var i; + for (i = 0; i < arg.length; i++) { + if (!type.check(arg[i])) { + _toss(msg, expected, type.operator, arg, type.actual); + } + } + }; + }); + + /* re-export built-in assertions */ + Object.keys(assert).forEach(function (k) { + if (k === 'AssertionError') { + out[k] = assert[k]; + return; + } + if (ndebug) { + out[k] = noop; + return; + } + out[k] = assert[k]; + }); + + /* export ourselves (for unit tests _only_) */ + out._setExports = _setExports; + + return out; +} + +module.exports = _setExports(process.env.NODE_NDEBUG); + + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); +if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sortAlpha = sortAlpha; +exports.sortOptionsByFlags = sortOptionsByFlags; +exports.entries = entries; +exports.removePrefix = removePrefix; +exports.removeSuffix = removeSuffix; +exports.addSuffix = addSuffix; +exports.hyphenate = hyphenate; +exports.camelCase = camelCase; +exports.compareSortedArrays = compareSortedArrays; +exports.sleep = sleep; +const _camelCase = __webpack_require__(230); + +function sortAlpha(a, b) { + // sort alphabetically in a deterministic way + const shortLen = Math.min(a.length, b.length); + for (let i = 0; i < shortLen; i++) { + const aChar = a.charCodeAt(i); + const bChar = b.charCodeAt(i); + if (aChar !== bChar) { + return aChar - bChar; + } + } + return a.length - b.length; +} + +function sortOptionsByFlags(a, b) { + const aOpt = a.flags.replace(/-/g, ''); + const bOpt = b.flags.replace(/-/g, ''); + return sortAlpha(aOpt, bOpt); +} + +function entries(obj) { + const entries = []; + if (obj) { + for (const key in obj) { + entries.push([key, obj[key]]); + } + } + return entries; +} + +function removePrefix(pattern, prefix) { + if (pattern.startsWith(prefix)) { + pattern = pattern.slice(prefix.length); + } + + return pattern; +} + +function removeSuffix(pattern, suffix) { + if (pattern.endsWith(suffix)) { + return pattern.slice(0, -suffix.length); + } + + return pattern; +} + +function addSuffix(pattern, suffix) { + if (!pattern.endsWith(suffix)) { + return pattern + suffix; + } + + return pattern; +} + +function hyphenate(str) { + return str.replace(/[A-Z]/g, match => { + return '-' + match.charAt(0).toLowerCase(); + }); +} + +function camelCase(str) { + if (/[A-Z]/.test(str)) { + return null; + } else { + return _camelCase(str); + } +} + +function compareSortedArrays(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + for (let i = 0, len = array1.length; i < len; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; +} + +function sleep(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.stringify = exports.parse = undefined; + +var _asyncToGenerator2; + +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); +} + +var _parse; + +function _load_parse() { + return _parse = __webpack_require__(105); +} + +Object.defineProperty(exports, 'parse', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_parse || _load_parse()).default; + } +}); + +var _stringify; + +function _load_stringify() { + return _stringify = __webpack_require__(199); +} + +Object.defineProperty(exports, 'stringify', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_stringify || _load_stringify()).default; + } +}); +exports.implodeEntry = implodeEntry; +exports.explodeEntry = explodeEntry; + +var _misc; + +function _load_misc() { + return _misc = __webpack_require__(18); +} + +var _normalizePattern; + +function _load_normalizePattern() { + return _normalizePattern = __webpack_require__(37); +} + +var _parse2; + +function _load_parse2() { + return _parse2 = _interopRequireDefault(__webpack_require__(105)); +} + +var _constants; + +function _load_constants() { + return _constants = __webpack_require__(8); +} + +var _fs; + +function _load_fs() { + return _fs = _interopRequireWildcard(__webpack_require__(4)); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const invariant = __webpack_require__(9); + +const path = __webpack_require__(0); +const ssri = __webpack_require__(65); + +function getName(pattern) { + return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; +} + +function blankObjectUndefined(obj) { + return obj && Object.keys(obj).length ? obj : undefined; +} + +function keyForRemote(remote) { + return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); +} + +function serializeIntegrity(integrity) { + // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output + // See https://git.io/vx2Hy + return integrity.toString().split(' ').sort().join(' '); +} + +function implodeEntry(pattern, obj) { + const inferredName = getName(pattern); + const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; + const imploded = { + name: inferredName === obj.name ? undefined : obj.name, + version: obj.version, + uid: obj.uid === obj.version ? undefined : obj.uid, + resolved: obj.resolved, + registry: obj.registry === 'npm' ? undefined : obj.registry, + dependencies: blankObjectUndefined(obj.dependencies), + optionalDependencies: blankObjectUndefined(obj.optionalDependencies), + permissions: blankObjectUndefined(obj.permissions), + prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) + }; + if (integrity) { + imploded.integrity = integrity; + } + return imploded; +} + +function explodeEntry(pattern, obj) { + obj.optionalDependencies = obj.optionalDependencies || {}; + obj.dependencies = obj.dependencies || {}; + obj.uid = obj.uid || obj.version; + obj.permissions = obj.permissions || {}; + obj.registry = obj.registry || 'npm'; + obj.name = obj.name || getName(pattern); + const integrity = obj.integrity; + if (integrity && integrity.isIntegrity) { + obj.integrity = ssri.parse(integrity); + } + return obj; +} + +class Lockfile { + constructor({ cache, source, parseResultType } = {}) { + this.source = source || ''; + this.cache = cache; + this.parseResultType = parseResultType; + } + + // source string if the `cache` was parsed + + + // if true, we're parsing an old yarn file and need to update integrity fields + hasEntriesExistWithoutIntegrity() { + if (!this.cache) { + return false; + } + + for (const key in this.cache) { + // $FlowFixMe - `this.cache` is clearly defined at this point + if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { + return true; + } + } + + return false; + } + + static fromDirectory(dir, reporter) { + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // read the manifest in this directory + const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); + + let lockfile; + let rawLockfile = ''; + let parseResult; + + if (yield (_fs || _load_fs()).exists(lockfileLoc)) { + rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); + parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); + + if (reporter) { + if (parseResult.type === 'merge') { + reporter.info(reporter.lang('lockfileMerged')); + } else if (parseResult.type === 'conflict') { + reporter.warn(reporter.lang('lockfileConflict')); + } + } + + lockfile = parseResult.object; + } else if (reporter) { + reporter.info(reporter.lang('noLockfileFound')); + } + + if (lockfile && lockfile.__metadata) { + const lockfilev2 = lockfile; + lockfile = {}; + } + + return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); + })(); + } + + getLocked(pattern) { + const cache = this.cache; + if (!cache) { + return undefined; + } + + const shrunk = pattern in cache && cache[pattern]; + + if (typeof shrunk === 'string') { + return this.getLocked(shrunk); + } else if (shrunk) { + explodeEntry(pattern, shrunk); + return shrunk; + } + + return undefined; + } + + removePattern(pattern) { + const cache = this.cache; + if (!cache) { + return; + } + delete cache[pattern]; + } + + getLockfile(patterns) { + const lockfile = {}; + const seen = new Map(); + + // order by name so that lockfile manifest is assigned to the first dependency with this manifest + // the others that have the same remoteKey will just refer to the first + // ordering allows for consistency in lockfile when it is serialized + const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); + + for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + const pattern = _ref; + + const pkg = patterns[pattern]; + const remote = pkg._remote, + ref = pkg._reference; + + invariant(ref, 'Package is missing a reference'); + invariant(remote, 'Package is missing a remote'); + + const remoteKey = keyForRemote(remote); + const seenPattern = remoteKey && seen.get(remoteKey); + if (seenPattern) { + // no point in duplicating it + lockfile[pattern] = seenPattern; + + // if we're relying on our name being inferred and two of the patterns have + // different inferred names then we need to set it + if (!seenPattern.name && getName(pattern) !== pkg.name) { + seenPattern.name = pkg.name; + } + continue; + } + const obj = implodeEntry(pattern, { + name: pkg.name, + version: pkg.version, + uid: pkg._uid, + resolved: remote.resolved, + integrity: remote.integrity, + registry: remote.registry, + dependencies: pkg.dependencies, + peerDependencies: pkg.peerDependencies, + optionalDependencies: pkg.optionalDependencies, + permissions: ref.permissions, + prebuiltVariants: pkg.prebuiltVariants + }); + + lockfile[pattern] = obj; + + if (remoteKey) { + seen.set(remoteKey, obj); + } + } + + return lockfile; + } +} +exports.default = Lockfile; + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +var store = __webpack_require__(133)('wks'); +var uid = __webpack_require__(137); +var Symbol = __webpack_require__(17).Symbol; +var USE_SYMBOL = typeof Symbol == 'function'; + +var $exports = module.exports = function (name) { + return store[name] || (store[name] = + USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); +}; + +$exports.store = store; + + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +exports.__esModule = true; + +var _assign = __webpack_require__(591); + +var _assign2 = _interopRequireDefault(_assign); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = _assign2.default || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; +}; + +/***/ }), +/* 22 */ +/***/ (function(module, exports) { + +exports = module.exports = SemVer; + +// The debug function is excluded entirely from the minified version. +/* nomin */ var debug; +/* nomin */ if (typeof process === 'object' && + /* nomin */ process.env && + /* nomin */ process.env.NODE_DEBUG && + /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) + /* nomin */ debug = function() { + /* nomin */ var args = Array.prototype.slice.call(arguments, 0); + /* nomin */ args.unshift('SEMVER'); + /* nomin */ console.log.apply(console, args); + /* nomin */ }; +/* nomin */ else + /* nomin */ debug = function() {}; + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0'; + +var MAX_LENGTH = 256; +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16; + +// The actual regexps go on exports.re +var re = exports.re = []; +var src = exports.src = []; +var R = 0; + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +var NUMERICIDENTIFIER = R++; +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; +var NUMERICIDENTIFIERLOOSE = R++; +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; + + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +var NONNUMERICIDENTIFIER = R++; +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; + + +// ## Main Version +// Three dot-separated numeric identifiers. + +var MAINVERSION = R++; +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')'; + +var MAINVERSIONLOOSE = R++; +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +var PRERELEASEIDENTIFIER = R++; +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + +var PRERELEASEIDENTIFIERLOOSE = R++; +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +var PRERELEASE = R++; +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; + +var PRERELEASELOOSE = R++; +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +var BUILDIDENTIFIER = R++; +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +var BUILD = R++; +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; + + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +var FULL = R++; +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?'; + +src[FULL] = '^' + FULLPLAIN + '$'; + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?'; + +var LOOSE = R++; +src[LOOSE] = '^' + LOOSEPLAIN + '$'; + +var GTLT = R++; +src[GTLT] = '((?:<|>)?=?)'; + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++; +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; +var XRANGEIDENTIFIER = R++; +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; + +var XRANGEPLAIN = R++; +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?'; + +var XRANGEPLAINLOOSE = R++; +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?'; + +var XRANGE = R++; +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; +var XRANGELOOSE = R++; +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +var COERCE = R++; +src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])'; + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++; +src[LONETILDE] = '(?:~>?)'; + +var TILDETRIM = R++; +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); +var tildeTrimReplace = '$1~'; + +var TILDE = R++; +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; +var TILDELOOSE = R++; +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++; +src[LONECARET] = '(?:\\^)'; + +var CARETTRIM = R++; +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); +var caretTrimReplace = '$1^'; + +var CARET = R++; +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; +var CARETLOOSE = R++; +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++; +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; +var COMPARATOR = R++; +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; + + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++; +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; + +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); +var comparatorTrimReplace = '$1$2$3'; + + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++; +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$'; + +var HYPHENRANGELOOSE = R++; +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$'; + +// Star ranges basically just allow anything at all. +var STAR = R++; +src[STAR] = '(<|>)?=?\\s*\\*'; + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]); + if (!re[i]) + re[i] = new RegExp(src[i]); +} + +exports.parse = parse; +function parse(version, loose) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + if (version.length > MAX_LENGTH) + return null; + + var r = loose ? re[LOOSE] : re[FULL]; + if (!r.test(version)) + return null; + + try { + return new SemVer(version, loose); + } catch (er) { + return null; + } +} + +exports.valid = valid; +function valid(version, loose) { + var v = parse(version, loose); + return v ? v.version : null; +} + + +exports.clean = clean; +function clean(version, loose) { + var s = parse(version.trim().replace(/^[=v]+/, ''), loose); + return s ? s.version : null; +} + +exports.SemVer = SemVer; + +function SemVer(version, loose) { + if (version instanceof SemVer) { + if (version.loose === loose) + return version; + else + version = version.version; + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version); + } + + if (version.length > MAX_LENGTH) + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + + if (!(this instanceof SemVer)) + return new SemVer(version, loose); + + debug('SemVer', version, loose); + this.loose = loose; + var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); + + if (!m) + throw new TypeError('Invalid Version: ' + version); + + this.raw = version; + + // these are actually numbers + this.major = +m[1]; + this.minor = +m[2]; + this.patch = +m[3]; + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) + throw new TypeError('Invalid major version') + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) + throw new TypeError('Invalid minor version') + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) + throw new TypeError('Invalid patch version') + + // numberify any prerelease numeric ids + if (!m[4]) + this.prerelease = []; + else + this.prerelease = m[4].split('.').map(function(id) { + if (/^[0-9]+$/.test(id)) { + var num = +id; + if (num >= 0 && num < MAX_SAFE_INTEGER) + return num; + } + return id; + }); + + this.build = m[5] ? m[5].split('.') : []; + this.format(); +} + +SemVer.prototype.format = function() { + this.version = this.major + '.' + this.minor + '.' + this.patch; + if (this.prerelease.length) + this.version += '-' + this.prerelease.join('.'); + return this.version; +}; + +SemVer.prototype.toString = function() { + return this.version; +}; + +SemVer.prototype.compare = function(other) { + debug('SemVer.compare', this.version, this.loose, other); + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return this.compareMain(other) || this.comparePre(other); +}; + +SemVer.prototype.compareMain = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch); +}; + +SemVer.prototype.comparePre = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) + return -1; + else if (!this.prerelease.length && other.prerelease.length) + return 1; + else if (!this.prerelease.length && !other.prerelease.length) + return 0; + + var i = 0; + do { + var a = this.prerelease[i]; + var b = other.prerelease[i]; + debug('prerelease compare', i, a, b); + if (a === undefined && b === undefined) + return 0; + else if (b === undefined) + return 1; + else if (a === undefined) + return -1; + else if (a === b) + continue; + else + return compareIdentifiers(a, b); + } while (++i); +}; + +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function(release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0; + this.patch = 0; + this.minor = 0; + this.major++; + this.inc('pre', identifier); + break; + case 'preminor': + this.prerelease.length = 0; + this.patch = 0; + this.minor++; + this.inc('pre', identifier); + break; + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0; + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) + this.major++; + this.minor = 0; + this.patch = 0; + this.prerelease = []; + break; + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) + this.minor++; + this.patch = 0; + this.prerelease = []; + break; + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) + this.patch++; + this.prerelease = []; + break; + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) + this.prerelease = [0]; + else { + var i = this.prerelease.length; + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++; + i = -2; + } + } + if (i === -1) // didn't increment anything + this.prerelease.push(0); + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) + this.prerelease = [identifier, 0]; + } else + this.prerelease = [identifier, 0]; + } + break; + + default: + throw new Error('invalid increment argument: ' + release); + } + this.format(); + this.raw = this.version; + return this; +}; + +exports.inc = inc; +function inc(version, release, loose, identifier) { + if (typeof(loose) === 'string') { + identifier = loose; + loose = undefined; + } + + try { + return new SemVer(version, loose).inc(release, identifier).version; + } catch (er) { + return null; + } +} + +exports.diff = diff; +function diff(version1, version2) { + if (eq(version1, version2)) { + return null; + } else { + var v1 = parse(version1); + var v2 = parse(version2); + if (v1.prerelease.length || v2.prerelease.length) { + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return 'pre'+key; + } + } + } + return 'prerelease'; + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return key; + } + } + } + } +} + +exports.compareIdentifiers = compareIdentifiers; + +var numeric = /^[0-9]+$/; +function compareIdentifiers(a, b) { + var anum = numeric.test(a); + var bnum = numeric.test(b); + + if (anum && bnum) { + a = +a; + b = +b; + } + + return (anum && !bnum) ? -1 : + (bnum && !anum) ? 1 : + a < b ? -1 : + a > b ? 1 : + 0; +} + +exports.rcompareIdentifiers = rcompareIdentifiers; +function rcompareIdentifiers(a, b) { + return compareIdentifiers(b, a); +} + +exports.major = major; +function major(a, loose) { + return new SemVer(a, loose).major; +} + +exports.minor = minor; +function minor(a, loose) { + return new SemVer(a, loose).minor; +} + +exports.patch = patch; +function patch(a, loose) { + return new SemVer(a, loose).patch; +} + +exports.compare = compare; +function compare(a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)); +} + +exports.compareLoose = compareLoose; +function compareLoose(a, b) { + return compare(a, b, true); +} + +exports.rcompare = rcompare; +function rcompare(a, b, loose) { + return compare(b, a, loose); +} + +exports.sort = sort; +function sort(list, loose) { + return list.sort(function(a, b) { + return exports.compare(a, b, loose); + }); +} + +exports.rsort = rsort; +function rsort(list, loose) { + return list.sort(function(a, b) { + return exports.rcompare(a, b, loose); + }); +} + +exports.gt = gt; +function gt(a, b, loose) { + return compare(a, b, loose) > 0; +} + +exports.lt = lt; +function lt(a, b, loose) { + return compare(a, b, loose) < 0; +} + +exports.eq = eq; +function eq(a, b, loose) { + return compare(a, b, loose) === 0; +} + +exports.neq = neq; +function neq(a, b, loose) { + return compare(a, b, loose) !== 0; +} + +exports.gte = gte; +function gte(a, b, loose) { + return compare(a, b, loose) >= 0; +} + +exports.lte = lte; +function lte(a, b, loose) { + return compare(a, b, loose) <= 0; +} + +exports.cmp = cmp; +function cmp(a, op, b, loose) { + var ret; + switch (op) { + case '===': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a === b; + break; + case '!==': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a !== b; + break; + case '': case '=': case '==': ret = eq(a, b, loose); break; + case '!=': ret = neq(a, b, loose); break; + case '>': ret = gt(a, b, loose); break; + case '>=': ret = gte(a, b, loose); break; + case '<': ret = lt(a, b, loose); break; + case '<=': ret = lte(a, b, loose); break; + default: throw new TypeError('Invalid operator: ' + op); + } + return ret; +} + +exports.Comparator = Comparator; +function Comparator(comp, loose) { + if (comp instanceof Comparator) { + if (comp.loose === loose) + return comp; + else + comp = comp.value; + } + + if (!(this instanceof Comparator)) + return new Comparator(comp, loose); + + debug('comparator', comp, loose); + this.loose = loose; + this.parse(comp); + + if (this.semver === ANY) + this.value = ''; + else + this.value = this.operator + this.semver.version; + + debug('comp', this); +} + +var ANY = {}; +Comparator.prototype.parse = function(comp) { + var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var m = comp.match(r); + + if (!m) + throw new TypeError('Invalid comparator: ' + comp); + + this.operator = m[1]; + if (this.operator === '=') + this.operator = ''; + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) + this.semver = ANY; + else + this.semver = new SemVer(m[2], this.loose); +}; + +Comparator.prototype.toString = function() { + return this.value; +}; + +Comparator.prototype.test = function(version) { + debug('Comparator.test', version, this.loose); + + if (this.semver === ANY) + return true; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + return cmp(version, this.operator, this.semver, this.loose); +}; + +Comparator.prototype.intersects = function(comp, loose) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required'); + } + + var rangeTmp; + + if (this.operator === '') { + rangeTmp = new Range(comp.value, loose); + return satisfies(this.value, rangeTmp, loose); + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, loose); + return satisfies(comp.semver, rangeTmp, loose); + } + + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>'); + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<'); + var sameSemVer = this.semver.version === comp.semver.version; + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<='); + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, loose) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')); + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, loose) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')); + + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; +}; + + +exports.Range = Range; +function Range(range, loose) { + if (range instanceof Range) { + if (range.loose === loose) { + return range; + } else { + return new Range(range.raw, loose); + } + } + + if (range instanceof Comparator) { + return new Range(range.value, loose); + } + + if (!(this instanceof Range)) + return new Range(range, loose); + + this.loose = loose; + + // First, split based on boolean or || + this.raw = range; + this.set = range.split(/\s*\|\|\s*/).map(function(range) { + return this.parseRange(range.trim()); + }, this).filter(function(c) { + // throw out any that are not relevant for whatever reason + return c.length; + }); + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range); + } + + this.format(); +} + +Range.prototype.format = function() { + this.range = this.set.map(function(comps) { + return comps.join(' ').trim(); + }).join('||').trim(); + return this.range; +}; + +Range.prototype.toString = function() { + return this.range; +}; + +Range.prototype.parseRange = function(range) { + var loose = this.loose; + range = range.trim(); + debug('range', range, loose); + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; + range = range.replace(hr, hyphenReplace); + debug('hyphen replace', range); + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); + debug('comparator trim', range, re[COMPARATORTRIM]); + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace); + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace); + + // normalize spaces + range = range.split(/\s+/).join(' '); + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var set = range.split(' ').map(function(comp) { + return parseComparator(comp, loose); + }).join(' ').split(/\s+/); + if (this.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function(comp) { + return !!comp.match(compRe); + }); + } + set = set.map(function(comp) { + return new Comparator(comp, loose); + }); + + return set; +}; + +Range.prototype.intersects = function(range, loose) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required'); + } + + return this.set.some(function(thisComparators) { + return thisComparators.every(function(thisComparator) { + return range.set.some(function(rangeComparators) { + return rangeComparators.every(function(rangeComparator) { + return thisComparator.intersects(rangeComparator, loose); + }); + }); + }); + }); +}; + +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators; +function toComparators(range, loose) { + return new Range(range, loose).set.map(function(comp) { + return comp.map(function(c) { + return c.value; + }).join(' ').trim().split(' '); + }); +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator(comp, loose) { + debug('comp', comp); + comp = replaceCarets(comp, loose); + debug('caret', comp); + comp = replaceTildes(comp, loose); + debug('tildes', comp); + comp = replaceXRanges(comp, loose); + debug('xrange', comp); + comp = replaceStars(comp, loose); + debug('stars', comp); + return comp; +} + +function isX(id) { + return !id || id.toLowerCase() === 'x' || id === '*'; +} + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceTilde(comp, loose); + }).join(' '); +} + +function replaceTilde(comp, loose) { + var r = loose ? re[TILDELOOSE] : re[TILDE]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + else if (isX(p)) + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + else if (pr) { + debug('replaceTilde pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; + + debug('tilde return', ret); + return ret; + }); +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceCaret(comp, loose); + }).join(' '); +} + +function replaceCaret(comp, loose) { + debug('caret', comp, loose); + var r = loose ? re[CARETLOOSE] : re[CARET]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + else if (isX(p)) { + if (M === '0') + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + else + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; + } else if (pr) { + debug('replaceCaret pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + if (M === '0') { + if (m === '0') + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + m + '.' + (+p + 1); + else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + (+M + 1) + '.0.0'; + } else { + debug('no pr'); + if (M === '0') { + if (m === '0') + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1); + else + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0'; + } + + debug('caret return', ret); + return ret; + }); +} + +function replaceXRanges(comp, loose) { + debug('replaceXRanges', comp, loose); + return comp.split(/\s+/).map(function(comp) { + return replaceXRange(comp, loose); + }).join(' '); +} + +function replaceXRange(comp, loose) { + comp = comp.trim(); + var r = loose ? re[XRANGELOOSE] : re[XRANGE]; + return comp.replace(r, function(ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr); + var xM = isX(M); + var xm = xM || isX(m); + var xp = xm || isX(p); + var anyX = xp; + + if (gtlt === '=' && anyX) + gtlt = ''; + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0'; + } else { + // nothing is forbidden + ret = '*'; + } + } else if (gtlt && anyX) { + // replace X with 0 + if (xm) + m = 0; + if (xp) + p = 0; + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>='; + if (xm) { + M = +M + 1; + m = 0; + p = 0; + } else if (xp) { + m = +m + 1; + p = 0; + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<'; + if (xm) + M = +M + 1; + else + m = +m + 1; + } + + ret = gtlt + M + '.' + m + '.' + p; + } else if (xm) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + } else if (xp) { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + } + + debug('xRange return', ret); + + return ret; + }); +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars(comp, loose) { + debug('replaceStars', comp, loose); + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], ''); +} + +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 +function hyphenReplace($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + + if (isX(fM)) + from = ''; + else if (isX(fm)) + from = '>=' + fM + '.0.0'; + else if (isX(fp)) + from = '>=' + fM + '.' + fm + '.0'; + else + from = '>=' + from; + + if (isX(tM)) + to = ''; + else if (isX(tm)) + to = '<' + (+tM + 1) + '.0.0'; + else if (isX(tp)) + to = '<' + tM + '.' + (+tm + 1) + '.0'; + else if (tpr) + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; + else + to = '<=' + to; + + return (from + ' ' + to).trim(); +} + + +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function(version) { + if (!version) + return false; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version)) + return true; + } + return false; +}; + +function testSet(set, version) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) + return false; + } + + if (version.prerelease.length) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (var i = 0; i < set.length; i++) { + debug(set[i].semver); + if (set[i].semver === ANY) + continue; + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver; + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) + return true; + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false; + } + + return true; +} + +exports.satisfies = satisfies; +function satisfies(version, range, loose) { + try { + range = new Range(range, loose); + } catch (er) { + return false; + } + return range.test(version); +} + +exports.maxSatisfying = maxSatisfying; +function maxSatisfying(versions, range, loose) { + var max = null; + var maxSV = null; + try { + var rangeObj = new Range(range, loose); + } catch (er) { + return null; + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { // satisfies(v, range, loose) + if (!max || maxSV.compare(v) === -1) { // compare(max, v, true) + max = v; + maxSV = new SemVer(max, loose); + } + } + }) + return max; +} + +exports.minSatisfying = minSatisfying; +function minSatisfying(versions, range, loose) { + var min = null; + var minSV = null; + try { + var rangeObj = new Range(range, loose); + } catch (er) { + return null; + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { // satisfies(v, range, loose) + if (!min || minSV.compare(v) === 1) { // compare(min, v, true) + min = v; + minSV = new SemVer(min, loose); + } + } + }) + return min; +} + +exports.validRange = validRange; +function validRange(range, loose) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, loose).range || '*'; + } catch (er) { + return null; + } +} + +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr; +function ltr(version, range, loose) { + return outside(version, range, '<', loose); +} + +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr; +function gtr(version, range, loose) { + return outside(version, range, '>', loose); +} + +exports.outside = outside; +function outside(version, range, hilo, loose) { + version = new SemVer(version, loose); + range = new Range(range, loose); + + var gtfn, ltefn, ltfn, comp, ecomp; + switch (hilo) { + case '>': + gtfn = gt; + ltefn = lte; + ltfn = lt; + comp = '>'; + ecomp = '>='; + break; + case '<': + gtfn = lt; + ltefn = gte; + ltfn = gt; + comp = '<'; + ecomp = '<='; + break; + default: + throw new TypeError('Must provide a hilo val of "<" or ">"'); + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, loose)) { + return false; + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i]; + + var high = null; + var low = null; + + comparators.forEach(function(comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator; + low = low || comparator; + if (gtfn(comparator.semver, high.semver, loose)) { + high = comparator; + } else if (ltfn(comparator.semver, low.semver, loose)) { + low = comparator; + } + }); + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false; + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false; + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false; + } + } + return true; +} + +exports.prerelease = prerelease; +function prerelease(version, loose) { + var parsed = parse(version, loose); + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null; +} + +exports.intersects = intersects; +function intersects(r1, r2, loose) { + r1 = new Range(r1, loose) + r2 = new Range(r2, loose) + return r1.intersects(r2) +} + +exports.coerce = coerce; +function coerce(version) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + var match = version.match(re[COERCE]); + + if (match == null) + return null; + + return parse((match[1] || '0') + '.' + (match[2] || '0') + '.' + (match[3] || '0')); +} + + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + +module.exports = require("stream"); + +/***/ }), +/* 24 */ +/***/ (function(module, exports) { + +module.exports = require("url"); + +/***/ }), +/* 25 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subscription; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__util_isArray__ = __webpack_require__(41); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__util_isObject__ = __webpack_require__(444); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__util_isFunction__ = __webpack_require__(154); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__util_tryCatch__ = __webpack_require__(56); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_errorObject__ = __webpack_require__(48); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__ = __webpack_require__(441); +/** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_tryCatch,_util_errorObject,_util_UnsubscriptionError PURE_IMPORTS_END */ + + + + + + +var Subscription = /*@__PURE__*/ (function () { + function Subscription(unsubscribe) { + this.closed = false; + this._parent = null; + this._parents = null; + this._subscriptions = null; + if (unsubscribe) { + this._unsubscribe = unsubscribe; + } + } + Subscription.prototype.unsubscribe = function () { + var hasErrors = false; + var errors; + if (this.closed) { + return; + } + var _a = this, _parent = _a._parent, _parents = _a._parents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; + this.closed = true; + this._parent = null; + this._parents = null; + this._subscriptions = null; + var index = -1; + var len = _parents ? _parents.length : 0; + while (_parent) { + _parent.remove(this); + _parent = ++index < len && _parents[index] || null; + } + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__util_isFunction__["a" /* isFunction */])(_unsubscribe)) { + var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(_unsubscribe).call(this); + if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { + hasErrors = true; + errors = errors || (__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */] ? + flattenUnsubscriptionErrors(__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e.errors) : [__WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e]); + } + } + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__util_isArray__["a" /* isArray */])(_subscriptions)) { + index = -1; + len = _subscriptions.length; + while (++index < len) { + var sub = _subscriptions[index]; + if (__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__util_isObject__["a" /* isObject */])(sub)) { + var trial = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_3__util_tryCatch__["a" /* tryCatch */])(sub.unsubscribe).call(sub); + if (trial === __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */]) { + hasErrors = true; + errors = errors || []; + var err = __WEBPACK_IMPORTED_MODULE_4__util_errorObject__["a" /* errorObject */].e; + if (err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) { + errors = errors.concat(flattenUnsubscriptionErrors(err.errors)); + } + else { + errors.push(err); + } + } + } + } + } + if (hasErrors) { + throw new __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */](errors); + } + }; + Subscription.prototype.add = function (teardown) { + if (!teardown || (teardown === Subscription.EMPTY)) { + return Subscription.EMPTY; + } + if (teardown === this) { + return this; + } + var subscription = teardown; + switch (typeof teardown) { + case 'function': + subscription = new Subscription(teardown); + case 'object': + if (subscription.closed || typeof subscription.unsubscribe !== 'function') { + return subscription; + } + else if (this.closed) { + subscription.unsubscribe(); + return subscription; + } + else if (typeof subscription._addParent !== 'function') { + var tmp = subscription; + subscription = new Subscription(); + subscription._subscriptions = [tmp]; + } + break; + default: + throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); + } + var subscriptions = this._subscriptions || (this._subscriptions = []); + subscriptions.push(subscription); + subscription._addParent(this); + return subscription; + }; + Subscription.prototype.remove = function (subscription) { + var subscriptions = this._subscriptions; + if (subscriptions) { + var subscriptionIndex = subscriptions.indexOf(subscription); + if (subscriptionIndex !== -1) { + subscriptions.splice(subscriptionIndex, 1); + } + } + }; + Subscription.prototype._addParent = function (parent) { + var _a = this, _parent = _a._parent, _parents = _a._parents; + if (!_parent || _parent === parent) { + this._parent = parent; + } + else if (!_parents) { + this._parents = [parent]; + } + else if (_parents.indexOf(parent) === -1) { + _parents.push(parent); + } + }; + Subscription.EMPTY = (function (empty) { + empty.closed = true; + return empty; + }(new Subscription())); + return Subscription; +}()); + +function flattenUnsubscriptionErrors(errors) { + return errors.reduce(function (errs, err) { return errs.concat((err instanceof __WEBPACK_IMPORTED_MODULE_5__util_UnsubscriptionError__["a" /* UnsubscriptionError */]) ? err.errors : err); }, []); +} +//# sourceMappingURL=Subscription.js.map + + +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +module.exports = { + bufferSplit: bufferSplit, + addRSAMissing: addRSAMissing, + calculateDSAPublic: calculateDSAPublic, + calculateED25519Public: calculateED25519Public, + calculateX25519Public: calculateX25519Public, + mpNormalize: mpNormalize, + mpDenormalize: mpDenormalize, + ecNormalize: ecNormalize, + countZeros: countZeros, + assertCompatible: assertCompatible, + isCompatible: isCompatible, + opensslKeyDeriv: opensslKeyDeriv, + opensshCipherInfo: opensshCipherInfo, + publicFromPrivateECDSA: publicFromPrivateECDSA, + zeroPadToLength: zeroPadToLength, + writeBitString: writeBitString, + readBitString: readBitString +}; + +var assert = __webpack_require__(16); +var Buffer = __webpack_require__(15).Buffer; +var PrivateKey = __webpack_require__(33); +var Key = __webpack_require__(27); +var crypto = __webpack_require__(11); +var algs = __webpack_require__(32); +var asn1 = __webpack_require__(66); + +var ec, jsbn; +var nacl; + +var MAX_CLASS_DEPTH = 3; + +function isCompatible(obj, klass, needVer) { + if (obj === null || typeof (obj) !== 'object') + return (false); + if (needVer === undefined) + needVer = klass.prototype._sshpkApiVersion; + if (obj instanceof klass && + klass.prototype._sshpkApiVersion[0] == needVer[0]) + return (true); + var proto = Object.getPrototypeOf(obj); + var depth = 0; + while (proto.constructor.name !== klass.name) { + proto = Object.getPrototypeOf(proto); + if (!proto || ++depth > MAX_CLASS_DEPTH) + return (false); + } + if (proto.constructor.name !== klass.name) + return (false); + var ver = proto._sshpkApiVersion; + if (ver === undefined) + ver = klass._oldVersionDetect(obj); + if (ver[0] != needVer[0] || ver[1] < needVer[1]) + return (false); + return (true); +} + +function assertCompatible(obj, klass, needVer, name) { + if (name === undefined) + name = 'object'; + assert.ok(obj, name + ' must not be null'); + assert.object(obj, name + ' must be an object'); + if (needVer === undefined) + needVer = klass.prototype._sshpkApiVersion; + if (obj instanceof klass && + klass.prototype._sshpkApiVersion[0] == needVer[0]) + return; + var proto = Object.getPrototypeOf(obj); + var depth = 0; + while (proto.constructor.name !== klass.name) { + proto = Object.getPrototypeOf(proto); + assert.ok(proto && ++depth <= MAX_CLASS_DEPTH, + name + ' must be a ' + klass.name + ' instance'); + } + assert.strictEqual(proto.constructor.name, klass.name, + name + ' must be a ' + klass.name + ' instance'); + var ver = proto._sshpkApiVersion; + if (ver === undefined) + ver = klass._oldVersionDetect(obj); + assert.ok(ver[0] == needVer[0] && ver[1] >= needVer[1], + name + ' must be compatible with ' + klass.name + ' klass ' + + 'version ' + needVer[0] + '.' + needVer[1]); +} + +var CIPHER_LEN = { + 'des-ede3-cbc': { key: 7, iv: 8 }, + 'aes-128-cbc': { key: 16, iv: 16 } +}; +var PKCS5_SALT_LEN = 8; + +function opensslKeyDeriv(cipher, salt, passphrase, count) { + assert.buffer(salt, 'salt'); + assert.buffer(passphrase, 'passphrase'); + assert.number(count, 'iteration count'); + + var clen = CIPHER_LEN[cipher]; + assert.object(clen, 'supported cipher'); + + salt = salt.slice(0, PKCS5_SALT_LEN); + + var D, D_prev, bufs; + var material = Buffer.alloc(0); + while (material.length < clen.key + clen.iv) { + bufs = []; + if (D_prev) + bufs.push(D_prev); + bufs.push(passphrase); + bufs.push(salt); + D = Buffer.concat(bufs); + for (var j = 0; j < count; ++j) + D = crypto.createHash('md5').update(D).digest(); + material = Buffer.concat([material, D]); + D_prev = D; + } + + return ({ + key: material.slice(0, clen.key), + iv: material.slice(clen.key, clen.key + clen.iv) + }); +} + +/* Count leading zero bits on a buffer */ +function countZeros(buf) { + var o = 0, obit = 8; + while (o < buf.length) { + var mask = (1 << obit); + if ((buf[o] & mask) === mask) + break; + obit--; + if (obit < 0) { + o++; + obit = 8; + } + } + return (o*8 + (8 - obit) - 1); +} + +function bufferSplit(buf, chr) { + assert.buffer(buf); + assert.string(chr); + + var parts = []; + var lastPart = 0; + var matches = 0; + for (var i = 0; i < buf.length; ++i) { + if (buf[i] === chr.charCodeAt(matches)) + ++matches; + else if (buf[i] === chr.charCodeAt(0)) + matches = 1; + else + matches = 0; + + if (matches >= chr.length) { + var newPart = i + 1; + parts.push(buf.slice(lastPart, newPart - matches)); + lastPart = newPart; + matches = 0; + } + } + if (lastPart <= buf.length) + parts.push(buf.slice(lastPart, buf.length)); + + return (parts); +} + +function ecNormalize(buf, addZero) { + assert.buffer(buf); + if (buf[0] === 0x00 && buf[1] === 0x04) { + if (addZero) + return (buf); + return (buf.slice(1)); + } else if (buf[0] === 0x04) { + if (!addZero) + return (buf); + } else { + while (buf[0] === 0x00) + buf = buf.slice(1); + if (buf[0] === 0x02 || buf[0] === 0x03) + throw (new Error('Compressed elliptic curve points ' + + 'are not supported')); + if (buf[0] !== 0x04) + throw (new Error('Not a valid elliptic curve point')); + if (!addZero) + return (buf); + } + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x0; + buf.copy(b, 1); + return (b); +} + +function readBitString(der, tag) { + if (tag === undefined) + tag = asn1.Ber.BitString; + var buf = der.readString(tag, true); + assert.strictEqual(buf[0], 0x00, 'bit strings with unused bits are ' + + 'not supported (0x' + buf[0].toString(16) + ')'); + return (buf.slice(1)); +} + +function writeBitString(der, buf, tag) { + if (tag === undefined) + tag = asn1.Ber.BitString; + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + der.writeBuffer(b, tag); +} + +function mpNormalize(buf) { + assert.buffer(buf); + while (buf.length > 1 && buf[0] === 0x00 && (buf[1] & 0x80) === 0x00) + buf = buf.slice(1); + if ((buf[0] & 0x80) === 0x80) { + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + buf = b; + } + return (buf); +} + +function mpDenormalize(buf) { + assert.buffer(buf); + while (buf.length > 1 && buf[0] === 0x00) + buf = buf.slice(1); + return (buf); +} + +function zeroPadToLength(buf, len) { + assert.buffer(buf); + assert.number(len); + while (buf.length > len) { + assert.equal(buf[0], 0x00); + buf = buf.slice(1); + } + while (buf.length < len) { + var b = Buffer.alloc(buf.length + 1); + b[0] = 0x00; + buf.copy(b, 1); + buf = b; + } + return (buf); +} + +function bigintToMpBuf(bigint) { + var buf = Buffer.from(bigint.toByteArray()); + buf = mpNormalize(buf); + return (buf); +} + +function calculateDSAPublic(g, p, x) { + assert.buffer(g); + assert.buffer(p); + assert.buffer(x); + try { + var bigInt = __webpack_require__(81).BigInteger; + } catch (e) { + throw (new Error('To load a PKCS#8 format DSA private key, ' + + 'the node jsbn library is required.')); + } + g = new bigInt(g); + p = new bigInt(p); + x = new bigInt(x); + var y = g.modPow(x, p); + var ybuf = bigintToMpBuf(y); + return (ybuf); +} + +function calculateED25519Public(k) { + assert.buffer(k); + + if (nacl === undefined) + nacl = __webpack_require__(76); + + var kp = nacl.sign.keyPair.fromSeed(new Uint8Array(k)); + return (Buffer.from(kp.publicKey)); +} + +function calculateX25519Public(k) { + assert.buffer(k); + + if (nacl === undefined) + nacl = __webpack_require__(76); + + var kp = nacl.box.keyPair.fromSeed(new Uint8Array(k)); + return (Buffer.from(kp.publicKey)); +} + +function addRSAMissing(key) { + assert.object(key); + assertCompatible(key, PrivateKey, [1, 1]); + try { + var bigInt = __webpack_require__(81).BigInteger; + } catch (e) { + throw (new Error('To write a PEM private key from ' + + 'this source, the node jsbn lib is required.')); + } + + var d = new bigInt(key.part.d.data); + var buf; + + if (!key.part.dmodp) { + var p = new bigInt(key.part.p.data); + var dmodp = d.mod(p.subtract(1)); + + buf = bigintToMpBuf(dmodp); + key.part.dmodp = {name: 'dmodp', data: buf}; + key.parts.push(key.part.dmodp); + } + if (!key.part.dmodq) { + var q = new bigInt(key.part.q.data); + var dmodq = d.mod(q.subtract(1)); + + buf = bigintToMpBuf(dmodq); + key.part.dmodq = {name: 'dmodq', data: buf}; + key.parts.push(key.part.dmodq); + } +} + +function publicFromPrivateECDSA(curveName, priv) { + assert.string(curveName, 'curveName'); + assert.buffer(priv); + if (ec === undefined) + ec = __webpack_require__(139); + if (jsbn === undefined) + jsbn = __webpack_require__(81).BigInteger; + var params = algs.curves[curveName]; + var p = new jsbn(params.p); + var a = new jsbn(params.a); + var b = new jsbn(params.b); + var curve = new ec.ECCurveFp(p, a, b); + var G = curve.decodePointHex(params.G.toString('hex')); + + var d = new jsbn(mpNormalize(priv)); + var pub = G.multiply(d); + pub = Buffer.from(curve.encodePointHex(pub), 'hex'); + + var parts = []; + parts.push({name: 'curve', data: Buffer.from(curveName)}); + parts.push({name: 'Q', data: pub}); + + var key = new Key({type: 'ecdsa', curve: curve, parts: parts}); + return (key); +} + +function opensshCipherInfo(cipher) { + var inf = {}; + switch (cipher) { + case '3des-cbc': + inf.keySize = 24; + inf.blockSize = 8; + inf.opensslName = 'des-ede3-cbc'; + break; + case 'blowfish-cbc': + inf.keySize = 16; + inf.blockSize = 8; + inf.opensslName = 'bf-cbc'; + break; + case 'aes128-cbc': + case 'aes128-ctr': + case 'aes128-gcm@openssh.com': + inf.keySize = 16; + inf.blockSize = 16; + inf.opensslName = 'aes-128-' + cipher.slice(7, 10); + break; + case 'aes192-cbc': + case 'aes192-ctr': + case 'aes192-gcm@openssh.com': + inf.keySize = 24; + inf.blockSize = 16; + inf.opensslName = 'aes-192-' + cipher.slice(7, 10); + break; + case 'aes256-cbc': + case 'aes256-ctr': + case 'aes256-gcm@openssh.com': + inf.keySize = 32; + inf.blockSize = 16; + inf.opensslName = 'aes-256-' + cipher.slice(7, 10); + break; + default: + throw (new Error( + 'Unsupported openssl cipher "' + cipher + '"')); + } + return (inf); +} + + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = Key; + +var assert = __webpack_require__(16); +var algs = __webpack_require__(32); +var crypto = __webpack_require__(11); +var Fingerprint = __webpack_require__(156); +var Signature = __webpack_require__(75); +var DiffieHellman = __webpack_require__(325).DiffieHellman; +var errs = __webpack_require__(74); +var utils = __webpack_require__(26); +var PrivateKey = __webpack_require__(33); +var edCompat; + +try { + edCompat = __webpack_require__(454); +} catch (e) { + /* Just continue through, and bail out if we try to use it. */ +} + +var InvalidAlgorithmError = errs.InvalidAlgorithmError; +var KeyParseError = errs.KeyParseError; + +var formats = {}; +formats['auto'] = __webpack_require__(455); +formats['pem'] = __webpack_require__(86); +formats['pkcs1'] = __webpack_require__(327); +formats['pkcs8'] = __webpack_require__(157); +formats['rfc4253'] = __webpack_require__(103); +formats['ssh'] = __webpack_require__(456); +formats['ssh-private'] = __webpack_require__(192); +formats['openssh'] = formats['ssh-private']; +formats['dnssec'] = __webpack_require__(326); + +function Key(opts) { + assert.object(opts, 'options'); + assert.arrayOfObject(opts.parts, 'options.parts'); + assert.string(opts.type, 'options.type'); + assert.optionalString(opts.comment, 'options.comment'); + + var algInfo = algs.info[opts.type]; + if (typeof (algInfo) !== 'object') + throw (new InvalidAlgorithmError(opts.type)); + + var partLookup = {}; + for (var i = 0; i < opts.parts.length; ++i) { + var part = opts.parts[i]; + partLookup[part.name] = part; + } + + this.type = opts.type; + this.parts = opts.parts; + this.part = partLookup; + this.comment = undefined; + this.source = opts.source; + + /* for speeding up hashing/fingerprint operations */ + this._rfc4253Cache = opts._rfc4253Cache; + this._hashCache = {}; + + var sz; + this.curve = undefined; + if (this.type === 'ecdsa') { + var curve = this.part.curve.data.toString(); + this.curve = curve; + sz = algs.curves[curve].size; + } else if (this.type === 'ed25519' || this.type === 'curve25519') { + sz = 256; + this.curve = 'curve25519'; + } else { + var szPart = this.part[algInfo.sizePart]; + sz = szPart.data.length; + sz = sz * 8 - utils.countZeros(szPart.data); + } + this.size = sz; +} + +Key.formats = formats; + +Key.prototype.toBuffer = function (format, options) { + if (format === undefined) + format = 'ssh'; + assert.string(format, 'format'); + assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); + + if (format === 'rfc4253') { + if (this._rfc4253Cache === undefined) + this._rfc4253Cache = formats['rfc4253'].write(this); + return (this._rfc4253Cache); + } + + return (formats[format].write(this, options)); +}; + +Key.prototype.toString = function (format, options) { + return (this.toBuffer(format, options).toString()); +}; + +Key.prototype.hash = function (algo) { + assert.string(algo, 'algorithm'); + algo = algo.toLowerCase(); + if (algs.hashAlgs[algo] === undefined) + throw (new InvalidAlgorithmError(algo)); + + if (this._hashCache[algo]) + return (this._hashCache[algo]); + var hash = crypto.createHash(algo). + update(this.toBuffer('rfc4253')).digest(); + this._hashCache[algo] = hash; + return (hash); +}; + +Key.prototype.fingerprint = function (algo) { + if (algo === undefined) + algo = 'sha256'; + assert.string(algo, 'algorithm'); + var opts = { + type: 'key', + hash: this.hash(algo), + algorithm: algo + }; + return (new Fingerprint(opts)); +}; + +Key.prototype.defaultHashAlgorithm = function () { + var hashAlgo = 'sha1'; + if (this.type === 'rsa') + hashAlgo = 'sha256'; + if (this.type === 'dsa' && this.size > 1024) + hashAlgo = 'sha256'; + if (this.type === 'ed25519') + hashAlgo = 'sha512'; + if (this.type === 'ecdsa') { + if (this.size <= 256) + hashAlgo = 'sha256'; + else if (this.size <= 384) + hashAlgo = 'sha384'; + else + hashAlgo = 'sha512'; + } + return (hashAlgo); +}; + +Key.prototype.createVerify = function (hashAlgo) { + if (hashAlgo === undefined) + hashAlgo = this.defaultHashAlgorithm(); + assert.string(hashAlgo, 'hash algorithm'); + + /* ED25519 is not supported by OpenSSL, use a javascript impl. */ + if (this.type === 'ed25519' && edCompat !== undefined) + return (new edCompat.Verifier(this, hashAlgo)); + if (this.type === 'curve25519') + throw (new Error('Curve25519 keys are not suitable for ' + + 'signing or verification')); + + var v, nm, err; + try { + nm = hashAlgo.toUpperCase(); + v = crypto.createVerify(nm); + } catch (e) { + err = e; + } + if (v === undefined || (err instanceof Error && + err.message.match(/Unknown message digest/))) { + nm = 'RSA-'; + nm += hashAlgo.toUpperCase(); + v = crypto.createVerify(nm); + } + assert.ok(v, 'failed to create verifier'); + var oldVerify = v.verify.bind(v); + var key = this.toBuffer('pkcs8'); + var curve = this.curve; + var self = this; + v.verify = function (signature, fmt) { + if (Signature.isSignature(signature, [2, 0])) { + if (signature.type !== self.type) + return (false); + if (signature.hashAlgorithm && + signature.hashAlgorithm !== hashAlgo) + return (false); + if (signature.curve && self.type === 'ecdsa' && + signature.curve !== curve) + return (false); + return (oldVerify(key, signature.toBuffer('asn1'))); + + } else if (typeof (signature) === 'string' || + Buffer.isBuffer(signature)) { + return (oldVerify(key, signature, fmt)); + + /* + * Avoid doing this on valid arguments, walking the prototype + * chain can be quite slow. + */ + } else if (Signature.isSignature(signature, [1, 0])) { + throw (new Error('signature was created by too old ' + + 'a version of sshpk and cannot be verified')); + + } else { + throw (new TypeError('signature must be a string, ' + + 'Buffer, or Signature object')); + } + }; + return (v); +}; + +Key.prototype.createDiffieHellman = function () { + if (this.type === 'rsa') + throw (new Error('RSA keys do not support Diffie-Hellman')); + + return (new DiffieHellman(this)); +}; +Key.prototype.createDH = Key.prototype.createDiffieHellman; + +Key.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + if (k instanceof PrivateKey) + k = k.toPublic(); + if (!k.comment) + k.comment = options.filename; + return (k); + } catch (e) { + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); + } +}; + +Key.isKey = function (obj, ver) { + return (utils.isCompatible(obj, Key, ver)); +}; + +/* + * API versions for Key: + * [1,0] -- initial ver, may take Signature for createVerify or may not + * [1,1] -- added pkcs1, pkcs8 formats + * [1,2] -- added auto, ssh-private, openssh formats + * [1,3] -- added defaultHashAlgorithm + * [1,4] -- added ed support, createDH + * [1,5] -- first explicitly tagged version + * [1,6] -- changed ed25519 part names + */ +Key.prototype._sshpkApiVersion = [1, 6]; + +Key._oldVersionDetect = function (obj) { + assert.func(obj.toBuffer); + assert.func(obj.fingerprint); + if (obj.createDH) + return ([1, 4]); + if (obj.defaultHashAlgorithm) + return ([1, 3]); + if (obj.formats['auto']) + return ([1, 2]); + if (obj.formats['pkcs1']) + return ([1, 1]); + return ([1, 0]); +}; + + +/***/ }), +/* 28 */ +/***/ (function(module, exports) { + +module.exports = require("assert"); + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = nullify; +function nullify(obj = {}) { + if (Array.isArray(obj)) { + for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + const item = _ref; + + nullify(item); + } + } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { + Object.setPrototypeOf(obj, null); + + // for..in can only be applied to 'object', not 'function' + if (typeof obj === 'object') { + for (const key in obj) { + nullify(obj[key]); + } + } + } + + return obj; +} + +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const escapeStringRegexp = __webpack_require__(388); +const ansiStyles = __webpack_require__(506); +const stdoutColor = __webpack_require__(598).stdout; + +const template = __webpack_require__(599); + +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); + +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; + +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); + +const styles = Object.create(null); + +function applyOptions(obj, options) { + options = options || {}; + + // Detect level if not set manually + const scLevel = stdoutColor ? stdoutColor.level : 0; + obj.level = options.level === undefined ? scLevel : options.level; + obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; +} + +function Chalk(options) { + // We check for this.template here since calling `chalk.constructor()` + // by itself will have a `this` of a previously constructed chalk object + if (!this || !(this instanceof Chalk) || this.template) { + const chalk = {}; + applyOptions(chalk, options); + + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; + + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); + + chalk.template.constructor = Chalk; + + return chalk.template; + } + + applyOptions(this, options); +} + +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; +} + +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); + + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; +} + +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); + } +}; + +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; + } + + styles[model] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.color.close, + closeRe: ansiStyles.color.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} + +ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); +for (const model of Object.keys(ansiStyles.bgColor.ansi)) { + if (skipModels.has(model)) { + continue; + } + + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.bgColor.close, + closeRe: ansiStyles.bgColor.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} + +const proto = Object.defineProperties(() => {}, styles); + +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; + + builder._styles = _styles; + builder._empty = _empty; + + const self = this; + + Object.defineProperty(builder, 'level', { + enumerable: true, + get() { + return self.level; + }, + set(level) { + self.level = level; + } + }); + + Object.defineProperty(builder, 'enabled', { + enumerable: true, + get() { + return self.enabled; + }, + set(enabled) { + self.enabled = enabled; + } + }); + + // See below for fix regarding invisible grey/dim combination on Windows + builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto + + return builder; +} + +function applyStyle() { + // Support varags, but simply cast to string in case there's only one arg + const args = arguments; + const argsLen = args.length; + let str = String(arguments[0]); + + if (argsLen === 0) { + return ''; + } + + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } + + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } + + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + const originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && this.hasGrey) { + ansiStyles.dim.open = ''; + } + + for (const code of this._styles.slice().reverse()) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; + + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); + } + + // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue + ansiStyles.dim.open = originalDim; + + return str; +} + +function chalkTag(chalk, strings) { + if (!Array.isArray(strings)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return [].slice.call(arguments, 1).join(' '); + } + + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; + + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } + + return template(chalk, parts.join('')); +} + +Object.defineProperties(Chalk.prototype, styles); + +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript + + +/***/ }), +/* 31 */ +/***/ (function(module, exports) { + +var core = module.exports = { version: '2.5.7' }; +if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright 2015 Joyent, Inc. + +var Buffer = __webpack_require__(15).Buffer; + +var algInfo = { + 'dsa': { + parts: ['p', 'q', 'g', 'y'], + sizePart: 'p' + }, + 'rsa': { + parts: ['e', 'n'], + sizePart: 'n' + }, + 'ecdsa': { + parts: ['curve', 'Q'], + sizePart: 'Q' + }, + 'ed25519': { + parts: ['A'], + sizePart: 'A' + } +}; +algInfo['curve25519'] = algInfo['ed25519']; + +var algPrivInfo = { + 'dsa': { + parts: ['p', 'q', 'g', 'y', 'x'] + }, + 'rsa': { + parts: ['n', 'e', 'd', 'iqmp', 'p', 'q'] + }, + 'ecdsa': { + parts: ['curve', 'Q', 'd'] + }, + 'ed25519': { + parts: ['A', 'k'] + } +}; +algPrivInfo['curve25519'] = algPrivInfo['ed25519']; + +var hashAlgs = { + 'md5': true, + 'sha1': true, + 'sha256': true, + 'sha384': true, + 'sha512': true +}; + +/* + * Taken from + * http://csrc.nist.gov/groups/ST/toolkit/documents/dss/NISTReCur.pdf + */ +var curves = { + 'nistp256': { + size: 256, + pkcs8oid: '1.2.840.10045.3.1.7', + p: Buffer.from(('00' + + 'ffffffff 00000001 00000000 00000000' + + '00000000 ffffffff ffffffff ffffffff'). + replace(/ /g, ''), 'hex'), + a: Buffer.from(('00' + + 'FFFFFFFF 00000001 00000000 00000000' + + '00000000 FFFFFFFF FFFFFFFF FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(( + '5ac635d8 aa3a93e7 b3ebbd55 769886bc' + + '651d06b0 cc53b0f6 3bce3c3e 27d2604b'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'c49d3608 86e70493 6a6678e1 139d26b7' + + '819f7e90'). + replace(/ /g, ''), 'hex'), + n: Buffer.from(('00' + + 'ffffffff 00000000 ffffffff ffffffff' + + 'bce6faad a7179e84 f3b9cac2 fc632551'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + '6b17d1f2 e12c4247 f8bce6e5 63a440f2' + + '77037d81 2deb33a0 f4a13945 d898c296' + + '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16' + + '2bce3357 6b315ece cbb64068 37bf51f5'). + replace(/ /g, ''), 'hex') + }, + 'nistp384': { + size: 384, + pkcs8oid: '1.3.132.0.34', + p: Buffer.from(('00' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff fffffffe' + + 'ffffffff 00000000 00000000 ffffffff'). + replace(/ /g, ''), 'hex'), + a: Buffer.from(('00' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE' + + 'FFFFFFFF 00000000 00000000 FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(( + 'b3312fa7 e23ee7e4 988e056b e3f82d19' + + '181d9c6e fe814112 0314088f 5013875a' + + 'c656398d 8a2ed19d 2a85c8ed d3ec2aef'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'a335926a a319a27a 1d00896a 6773a482' + + '7acdac73'). + replace(/ /g, ''), 'hex'), + n: Buffer.from(('00' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff c7634d81 f4372ddf' + + '581a0db2 48b0a77a ecec196a ccc52973'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + 'aa87ca22 be8b0537 8eb1c71e f320ad74' + + '6e1d3b62 8ba79b98 59f741e0 82542a38' + + '5502f25d bf55296c 3a545e38 72760ab7' + + '3617de4a 96262c6f 5d9e98bf 9292dc29' + + 'f8f41dbd 289a147c e9da3113 b5f0b8c0' + + '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f'). + replace(/ /g, ''), 'hex') + }, + 'nistp521': { + size: 521, + pkcs8oid: '1.3.132.0.35', + p: Buffer.from(( + '01ffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffff').replace(/ /g, ''), 'hex'), + a: Buffer.from(('01FF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF' + + 'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFC'). + replace(/ /g, ''), 'hex'), + b: Buffer.from(('51' + + '953eb961 8e1c9a1f 929a21a0 b68540ee' + + 'a2da725b 99b315f3 b8b48991 8ef109e1' + + '56193951 ec7e937b 1652c0bd 3bb1bf07' + + '3573df88 3d2c34f1 ef451fd4 6b503f00'). + replace(/ /g, ''), 'hex'), + s: Buffer.from(('00' + + 'd09e8800 291cb853 96cc6717 393284aa' + + 'a0da64ba').replace(/ /g, ''), 'hex'), + n: Buffer.from(('01ff' + + 'ffffffff ffffffff ffffffff ffffffff' + + 'ffffffff ffffffff ffffffff fffffffa' + + '51868783 bf2f966b 7fcc0148 f709a5d0' + + '3bb5c9b8 899c47ae bb6fb71e 91386409'). + replace(/ /g, ''), 'hex'), + G: Buffer.from(('04' + + '00c6 858e06b7 0404e9cd 9e3ecb66 2395b442' + + '9c648139 053fb521 f828af60 6b4d3dba' + + 'a14b5e77 efe75928 fe1dc127 a2ffa8de' + + '3348b3c1 856a429b f97e7e31 c2e5bd66' + + '0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9' + + '98f54449 579b4468 17afbd17 273e662c' + + '97ee7299 5ef42640 c550b901 3fad0761' + + '353c7086 a272c240 88be9476 9fd16650'). + replace(/ /g, ''), 'hex') + } +}; + +module.exports = { + info: algInfo, + privInfo: algPrivInfo, + hashAlgs: hashAlgs, + curves: curves +}; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +// Copyright 2017 Joyent, Inc. + +module.exports = PrivateKey; + +var assert = __webpack_require__(16); +var Buffer = __webpack_require__(15).Buffer; +var algs = __webpack_require__(32); +var crypto = __webpack_require__(11); +var Fingerprint = __webpack_require__(156); +var Signature = __webpack_require__(75); +var errs = __webpack_require__(74); +var util = __webpack_require__(3); +var utils = __webpack_require__(26); +var dhe = __webpack_require__(325); +var generateECDSA = dhe.generateECDSA; +var generateED25519 = dhe.generateED25519; +var edCompat; +var nacl; + +try { + edCompat = __webpack_require__(454); +} catch (e) { + /* Just continue through, and bail out if we try to use it. */ +} + +var Key = __webpack_require__(27); + +var InvalidAlgorithmError = errs.InvalidAlgorithmError; +var KeyParseError = errs.KeyParseError; +var KeyEncryptedError = errs.KeyEncryptedError; + +var formats = {}; +formats['auto'] = __webpack_require__(455); +formats['pem'] = __webpack_require__(86); +formats['pkcs1'] = __webpack_require__(327); +formats['pkcs8'] = __webpack_require__(157); +formats['rfc4253'] = __webpack_require__(103); +formats['ssh-private'] = __webpack_require__(192); +formats['openssh'] = formats['ssh-private']; +formats['ssh'] = formats['ssh-private']; +formats['dnssec'] = __webpack_require__(326); + +function PrivateKey(opts) { + assert.object(opts, 'options'); + Key.call(this, opts); + + this._pubCache = undefined; +} +util.inherits(PrivateKey, Key); + +PrivateKey.formats = formats; + +PrivateKey.prototype.toBuffer = function (format, options) { + if (format === undefined) + format = 'pkcs1'; + assert.string(format, 'format'); + assert.object(formats[format], 'formats[format]'); + assert.optionalObject(options, 'options'); + + return (formats[format].write(this, options)); +}; + +PrivateKey.prototype.hash = function (algo) { + return (this.toPublic().hash(algo)); +}; + +PrivateKey.prototype.toPublic = function () { + if (this._pubCache) + return (this._pubCache); + + var algInfo = algs.info[this.type]; + var pubParts = []; + for (var i = 0; i < algInfo.parts.length; ++i) { + var p = algInfo.parts[i]; + pubParts.push(this.part[p]); + } + + this._pubCache = new Key({ + type: this.type, + source: this, + parts: pubParts + }); + if (this.comment) + this._pubCache.comment = this.comment; + return (this._pubCache); +}; + +PrivateKey.prototype.derive = function (newType) { + assert.string(newType, 'type'); + var priv, pub, pair; + + if (this.type === 'ed25519' && newType === 'curve25519') { + if (nacl === undefined) + nacl = __webpack_require__(76); + + priv = this.part.k.data; + if (priv[0] === 0x00) + priv = priv.slice(1); + + pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); + pub = Buffer.from(pair.publicKey); + + return (new PrivateKey({ + type: 'curve25519', + parts: [ + { name: 'A', data: utils.mpNormalize(pub) }, + { name: 'k', data: utils.mpNormalize(priv) } + ] + })); + } else if (this.type === 'curve25519' && newType === 'ed25519') { + if (nacl === undefined) + nacl = __webpack_require__(76); + + priv = this.part.k.data; + if (priv[0] === 0x00) + priv = priv.slice(1); + + pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); + pub = Buffer.from(pair.publicKey); + + return (new PrivateKey({ + type: 'ed25519', + parts: [ + { name: 'A', data: utils.mpNormalize(pub) }, + { name: 'k', data: utils.mpNormalize(priv) } + ] + })); + } + throw (new Error('Key derivation not supported from ' + this.type + + ' to ' + newType)); +}; + +PrivateKey.prototype.createVerify = function (hashAlgo) { + return (this.toPublic().createVerify(hashAlgo)); +}; + +PrivateKey.prototype.createSign = function (hashAlgo) { + if (hashAlgo === undefined) + hashAlgo = this.defaultHashAlgorithm(); + assert.string(hashAlgo, 'hash algorithm'); + + /* ED25519 is not supported by OpenSSL, use a javascript impl. */ + if (this.type === 'ed25519' && edCompat !== undefined) + return (new edCompat.Signer(this, hashAlgo)); + if (this.type === 'curve25519') + throw (new Error('Curve25519 keys are not suitable for ' + + 'signing or verification')); + + var v, nm, err; + try { + nm = hashAlgo.toUpperCase(); + v = crypto.createSign(nm); + } catch (e) { + err = e; + } + if (v === undefined || (err instanceof Error && + err.message.match(/Unknown message digest/))) { + nm = 'RSA-'; + nm += hashAlgo.toUpperCase(); + v = crypto.createSign(nm); + } + assert.ok(v, 'failed to create verifier'); + var oldSign = v.sign.bind(v); + var key = this.toBuffer('pkcs1'); + var type = this.type; + var curve = this.curve; + v.sign = function () { + var sig = oldSign(key); + if (typeof (sig) === 'string') + sig = Buffer.from(sig, 'binary'); + sig = Signature.parse(sig, type, 'asn1'); + sig.hashAlgorithm = hashAlgo; + sig.curve = curve; + return (sig); + }; + return (v); +}; + +PrivateKey.parse = function (data, format, options) { + if (typeof (data) !== 'string') + assert.buffer(data, 'data'); + if (format === undefined) + format = 'auto'; + assert.string(format, 'format'); + if (typeof (options) === 'string') + options = { filename: options }; + assert.optionalObject(options, 'options'); + if (options === undefined) + options = {}; + assert.optionalString(options.filename, 'options.filename'); + if (options.filename === undefined) + options.filename = '(unnamed)'; + + assert.object(formats[format], 'formats[format]'); + + try { + var k = formats[format].read(data, options); + assert.ok(k instanceof PrivateKey, 'key is not a private key'); + if (!k.comment) + k.comment = options.filename; + return (k); + } catch (e) { + if (e.name === 'KeyEncryptedError') + throw (e); + throw (new KeyParseError(options.filename, format, e)); + } +}; + +PrivateKey.isPrivateKey = function (obj, ver) { + return (utils.isCompatible(obj, PrivateKey, ver)); +}; + +PrivateKey.generate = function (type, options) { + if (options === undefined) + options = {}; + assert.object(options, 'options'); + + switch (type) { + case 'ecdsa': + if (options.curve === undefined) + options.curve = 'nistp256'; + assert.string(options.curve, 'options.curve'); + return (generateECDSA(options.curve)); + case 'ed25519': + return (generateED25519()); + default: + throw (new Error('Key generation not supported with key ' + + 'type "' + type + '"')); + } +}; + +/* + * API versions for PrivateKey: + * [1,0] -- initial ver + * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats + * [1,2] -- added defaultHashAlgorithm + * [1,3] -- added derive, ed, createDH + * [1,4] -- first tagged version + * [1,5] -- changed ed25519 part names and format + */ +PrivateKey.prototype._sshpkApiVersion = [1, 5]; + +PrivateKey._oldVersionDetect = function (obj) { + assert.func(obj.toPublic); + assert.func(obj.createSign); + if (obj.derive) + return ([1, 3]); + if (obj.defaultHashAlgorithm) + return ([1, 2]); + if (obj.formats['auto']) + return ([1, 1]); + return ([1, 0]); +}; + + +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.wrapLifecycle = exports.run = exports.install = exports.Install = undefined; + +var _extends2; + +function _load_extends() { + return _extends2 = _interopRequireDefault(__webpack_require__(21)); +} + +var _asyncToGenerator2; + +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(2)); +} + +let install = exports.install = (() => { + var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, lockfile) { + yield wrapLifecycle(config, flags, (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const install = new Install(flags, config, reporter, lockfile); + yield install.init(); + })); + }); + + return function install(_x7, _x8, _x9, _x10) { + return _ref29.apply(this, arguments); + }; +})(); + +let run = exports.run = (() => { + var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) { + let lockfile; + let error = 'installCommandRenamed'; + if (flags.lockfile === false) { + lockfile = new (_lockfile || _load_lockfile()).default(); + } else { + lockfile = yield (_lockfile || _load_lockfile()).default.fromDirectory(config.lockfileFolder, reporter); + } + + if (args.length) { + const exampleArgs = args.slice(); + + if (flags.saveDev) { + exampleArgs.push('--dev'); + } + if (flags.savePeer) { + exampleArgs.push('--peer'); + } + if (flags.saveOptional) { + exampleArgs.push('--optional'); + } + if (flags.saveExact) { + exampleArgs.push('--exact'); + } + if (flags.saveTilde) { + exampleArgs.push('--tilde'); + } + let command = 'add'; + if (flags.global) { + error = 'globalFlagRemoved'; + command = 'global add'; + } + throw new (_errors || _load_errors()).MessageError(reporter.lang(error, `yarn ${command} ${exampleArgs.join(' ')}`)); + } + + yield install(config, reporter, flags, lockfile); + }); + + return function run(_x11, _x12, _x13, _x14) { + return _ref31.apply(this, arguments); + }; +})(); + +let wrapLifecycle = exports.wrapLifecycle = (() => { + var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, flags, factory) { + yield config.executeLifecycleScript('preinstall'); + + yield factory(); + + // npm behaviour, seems kinda funky but yay compatibility + yield config.executeLifecycleScript('install'); + yield config.executeLifecycleScript('postinstall'); + + if (!config.production) { + if (!config.disablePrepublish) { + yield config.executeLifecycleScript('prepublish'); + } + yield config.executeLifecycleScript('prepare'); + } + }); + + return function wrapLifecycle(_x15, _x16, _x17) { + return _ref32.apply(this, arguments); + }; +})(); + +exports.hasWrapper = hasWrapper; +exports.setFlags = setFlags; + +var _objectPath; + +function _load_objectPath() { + return _objectPath = _interopRequireDefault(__webpack_require__(304)); +} + +var _hooks; + +function _load_hooks() { + return _hooks = __webpack_require__(374); +} + +var _index; + +function _load_index() { + return _index = _interopRequireDefault(__webpack_require__(220)); +} + +var _errors; + +function _load_errors() { + return _errors = __webpack_require__(6); +} + +var _integrityChecker; + +function _load_integrityChecker() { + return _integrityChecker = _interopRequireDefault(__webpack_require__(208)); +} + +var _lockfile; + +function _load_lockfile() { + return _lockfile = _interopRequireDefault(__webpack_require__(19)); +} + +var _lockfile2; + +function _load_lockfile2() { + return _lockfile2 = __webpack_require__(19); +} + +var _packageFetcher; + +function _load_packageFetcher() { + return _packageFetcher = _interopRequireWildcard(__webpack_require__(210)); +} + +var _packageInstallScripts; + +function _load_packageInstallScripts() { + return _packageInstallScripts = _interopRequireDefault(__webpack_require__(557)); +} + +var _packageCompatibility; + +function _load_packageCompatibility() { + return _packageCompatibility = _interopRequireWildcard(__webpack_require__(209)); +} + +var _packageResolver; + +function _load_packageResolver() { + return _packageResolver = _interopRequireDefault(__webpack_require__(366)); +} + +var _packageLinker; + +function _load_packageLinker() { + return _packageLinker = _interopRequireDefault(__webpack_require__(211)); +} + +var _index2; + +function _load_index2() { + return _index2 = __webpack_require__(57); +} + +var _index3; + +function _load_index3() { + return _index3 = __webpack_require__(78); +} + +var _autoclean; + +function _load_autoclean() { + return _autoclean = __webpack_require__(354); +} + +var _constants; + +function _load_constants() { + return _constants = _interopRequireWildcard(__webpack_require__(8)); +} + +var _normalizePattern; + +function _load_normalizePattern() { + return _normalizePattern = __webpack_require__(37); +} + +var _fs; + +function _load_fs() { + return _fs = _interopRequireWildcard(__webpack_require__(4)); +} + +var _map; + +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(29)); +} + +var _yarnVersion; + +function _load_yarnVersion() { + return _yarnVersion = __webpack_require__(120); +} + +var _generatePnpMap; + +function _load_generatePnpMap() { + return _generatePnpMap = __webpack_require__(579); +} + +var _workspaceLayout; + +function _load_workspaceLayout() { + return _workspaceLayout = _interopRequireDefault(__webpack_require__(90)); +} + +var _resolutionMap; + +function _load_resolutionMap() { + return _resolutionMap = _interopRequireDefault(__webpack_require__(214)); +} + +var _guessName; + +function _load_guessName() { + return _guessName = _interopRequireDefault(__webpack_require__(169)); +} + +var _audit; + +function _load_audit() { + return _audit = _interopRequireDefault(__webpack_require__(353)); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const deepEqual = __webpack_require__(631); + +const emoji = __webpack_require__(302); +const invariant = __webpack_require__(9); +const path = __webpack_require__(0); +const semver = __webpack_require__(22); +const uuid = __webpack_require__(119); +const ssri = __webpack_require__(65); + +const ONE_DAY = 1000 * 60 * 60 * 24; + +/** + * Try and detect the installation method for Yarn and provide a command to update it with. + */ + +function getUpdateCommand(installationMethod) { + if (installationMethod === 'tar') { + return `curl --compressed -o- -L ${(_constants || _load_constants()).YARN_INSTALLER_SH} | bash`; + } + + if (installationMethod === 'homebrew') { + return 'brew upgrade yarn'; + } + + if (installationMethod === 'deb') { + return 'sudo apt-get update && sudo apt-get install yarn'; + } + + if (installationMethod === 'rpm') { + return 'sudo yum install yarn'; + } + + if (installationMethod === 'npm') { + return 'npm install --global yarn'; + } + + if (installationMethod === 'chocolatey') { + return 'choco upgrade yarn'; + } + + if (installationMethod === 'apk') { + return 'apk update && apk add -u yarn'; + } + + if (installationMethod === 'portage') { + return 'sudo emerge --sync && sudo emerge -au sys-apps/yarn'; + } + + return null; +} + +function getUpdateInstaller(installationMethod) { + // Windows + if (installationMethod === 'msi') { + return (_constants || _load_constants()).YARN_INSTALLER_MSI; + } + + return null; +} + +function normalizeFlags(config, rawFlags) { + const flags = { + // install + har: !!rawFlags.har, + ignorePlatform: !!rawFlags.ignorePlatform, + ignoreEngines: !!rawFlags.ignoreEngines, + ignoreScripts: !!rawFlags.ignoreScripts, + ignoreOptional: !!rawFlags.ignoreOptional, + force: !!rawFlags.force, + flat: !!rawFlags.flat, + lockfile: rawFlags.lockfile !== false, + pureLockfile: !!rawFlags.pureLockfile, + updateChecksums: !!rawFlags.updateChecksums, + skipIntegrityCheck: !!rawFlags.skipIntegrityCheck, + frozenLockfile: !!rawFlags.frozenLockfile, + linkDuplicates: !!rawFlags.linkDuplicates, + checkFiles: !!rawFlags.checkFiles, + audit: !!rawFlags.audit, + + // add + peer: !!rawFlags.peer, + dev: !!rawFlags.dev, + optional: !!rawFlags.optional, + exact: !!rawFlags.exact, + tilde: !!rawFlags.tilde, + ignoreWorkspaceRootCheck: !!rawFlags.ignoreWorkspaceRootCheck, + + // outdated, update-interactive + includeWorkspaceDeps: !!rawFlags.includeWorkspaceDeps, + + // add, remove, update + workspaceRootIsCwd: rawFlags.workspaceRootIsCwd !== false + }; + + if (config.getOption('ignore-scripts')) { + flags.ignoreScripts = true; + } + + if (config.getOption('ignore-platform')) { + flags.ignorePlatform = true; + } + + if (config.getOption('ignore-engines')) { + flags.ignoreEngines = true; + } + + if (config.getOption('ignore-optional')) { + flags.ignoreOptional = true; + } + + if (config.getOption('force')) { + flags.force = true; + } + + return flags; +} + +class Install { + constructor(flags, config, reporter, lockfile) { + this.rootManifestRegistries = []; + this.rootPatternsToOrigin = (0, (_map || _load_map()).default)(); + this.lockfile = lockfile; + this.reporter = reporter; + this.config = config; + this.flags = normalizeFlags(config, flags); + this.resolutions = (0, (_map || _load_map()).default)(); // Legacy resolutions field used for flat install mode + this.resolutionMap = new (_resolutionMap || _load_resolutionMap()).default(config); // Selective resolutions for nested dependencies + this.resolver = new (_packageResolver || _load_packageResolver()).default(config, lockfile, this.resolutionMap); + this.integrityChecker = new (_integrityChecker || _load_integrityChecker()).default(config); + this.linker = new (_packageLinker || _load_packageLinker()).default(config, this.resolver); + this.scripts = new (_packageInstallScripts || _load_packageInstallScripts()).default(config, this.resolver, this.flags.force); + } + + /** + * Create a list of dependency requests from the current directories manifests. + */ + + fetchRequestFromCwd(excludePatterns = [], ignoreUnusedPatterns = false) { + var _this = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const patterns = []; + const deps = []; + let resolutionDeps = []; + const manifest = {}; + + const ignorePatterns = []; + const usedPatterns = []; + let workspaceLayout; + + // some commands should always run in the context of the entire workspace + const cwd = _this.flags.includeWorkspaceDeps || _this.flags.workspaceRootIsCwd ? _this.config.lockfileFolder : _this.config.cwd; + + // non-workspaces are always root, otherwise check for workspace root + const cwdIsRoot = !_this.config.workspaceRootFolder || _this.config.lockfileFolder === cwd; + + // exclude package names that are in install args + const excludeNames = []; + for (var _iterator = excludePatterns, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + const pattern = _ref; + + if ((0, (_index3 || _load_index3()).getExoticResolver)(pattern)) { + excludeNames.push((0, (_guessName || _load_guessName()).default)(pattern)); + } else { + // extract the name + const parts = (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern); + excludeNames.push(parts.name); + } + } + + const stripExcluded = function stripExcluded(manifest) { + for (var _iterator2 = excludeNames, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref2; + + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref2 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref2 = _i2.value; + } + + const exclude = _ref2; + + if (manifest.dependencies && manifest.dependencies[exclude]) { + delete manifest.dependencies[exclude]; + } + if (manifest.devDependencies && manifest.devDependencies[exclude]) { + delete manifest.devDependencies[exclude]; + } + if (manifest.optionalDependencies && manifest.optionalDependencies[exclude]) { + delete manifest.optionalDependencies[exclude]; + } + } + }; + + for (var _iterator3 = Object.keys((_index2 || _load_index2()).registries), _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref3; + + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref3 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref3 = _i3.value; + } + + const registry = _ref3; + + const filename = (_index2 || _load_index2()).registries[registry].filename; + + const loc = path.join(cwd, filename); + if (!(yield (_fs || _load_fs()).exists(loc))) { + continue; + } + + _this.rootManifestRegistries.push(registry); + + const projectManifestJson = yield _this.config.readJson(loc); + yield (0, (_index || _load_index()).default)(projectManifestJson, cwd, _this.config, cwdIsRoot); + + Object.assign(_this.resolutions, projectManifestJson.resolutions); + Object.assign(manifest, projectManifestJson); + + _this.resolutionMap.init(_this.resolutions); + for (var _iterator4 = Object.keys(_this.resolutionMap.resolutionsByPackage), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref4; + + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref4 = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref4 = _i4.value; + } + + const packageName = _ref4; + + const optional = (_objectPath || _load_objectPath()).default.has(manifest.optionalDependencies, packageName) && _this.flags.ignoreOptional; + for (var _iterator8 = _this.resolutionMap.resolutionsByPackage[packageName], _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref9; + + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref9 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref9 = _i8.value; + } + + const _ref8 = _ref9; + const pattern = _ref8.pattern; + + resolutionDeps = [...resolutionDeps, { registry, pattern, optional, hint: 'resolution' }]; + } + } + + const pushDeps = function pushDeps(depType, manifest, { hint, optional }, isUsed) { + if (ignoreUnusedPatterns && !isUsed) { + return; + } + // We only take unused dependencies into consideration to get deterministic hoisting. + // Since flat mode doesn't care about hoisting and everything is top level and specified then we can safely + // leave these out. + if (_this.flags.flat && !isUsed) { + return; + } + const depMap = manifest[depType]; + for (const name in depMap) { + if (excludeNames.indexOf(name) >= 0) { + continue; + } + + let pattern = name; + if (!_this.lockfile.getLocked(pattern)) { + // when we use --save we save the dependency to the lockfile with just the name rather than the + // version combo + pattern += '@' + depMap[name]; + } + + // normalization made sure packages are mentioned only once + if (isUsed) { + usedPatterns.push(pattern); + } else { + ignorePatterns.push(pattern); + } + + _this.rootPatternsToOrigin[pattern] = depType; + patterns.push(pattern); + deps.push({ pattern, registry, hint, optional, workspaceName: manifest.name, workspaceLoc: manifest._loc }); + } + }; + + if (cwdIsRoot) { + pushDeps('dependencies', projectManifestJson, { hint: null, optional: false }, true); + pushDeps('devDependencies', projectManifestJson, { hint: 'dev', optional: false }, !_this.config.production); + pushDeps('optionalDependencies', projectManifestJson, { hint: 'optional', optional: true }, true); + } + + if (_this.config.workspaceRootFolder) { + const workspaceLoc = cwdIsRoot ? loc : path.join(_this.config.lockfileFolder, filename); + const workspacesRoot = path.dirname(workspaceLoc); + + let workspaceManifestJson = projectManifestJson; + if (!cwdIsRoot) { + // the manifest we read before was a child workspace, so get the root + workspaceManifestJson = yield _this.config.readJson(workspaceLoc); + yield (0, (_index || _load_index()).default)(workspaceManifestJson, workspacesRoot, _this.config, true); + } + + const workspaces = yield _this.config.resolveWorkspaces(workspacesRoot, workspaceManifestJson); + workspaceLayout = new (_workspaceLayout || _load_workspaceLayout()).default(workspaces, _this.config); + + // add virtual manifest that depends on all workspaces, this way package hoisters and resolvers will work fine + const workspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.dependencies); + for (var _iterator5 = Object.keys(workspaces), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref5; + + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref5 = _iterator5[_i5++]; + } else { + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref5 = _i5.value; + } + + const workspaceName = _ref5; + + const workspaceManifest = workspaces[workspaceName].manifest; + workspaceDependencies[workspaceName] = workspaceManifest.version; + + // include dependencies from all workspaces + if (_this.flags.includeWorkspaceDeps) { + pushDeps('dependencies', workspaceManifest, { hint: null, optional: false }, true); + pushDeps('devDependencies', workspaceManifest, { hint: 'dev', optional: false }, !_this.config.production); + pushDeps('optionalDependencies', workspaceManifest, { hint: 'optional', optional: true }, true); + } + } + const virtualDependencyManifest = { + _uid: '', + name: `workspace-aggregator-${uuid.v4()}`, + version: '1.0.0', + _registry: 'npm', + _loc: workspacesRoot, + dependencies: workspaceDependencies, + devDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.devDependencies), + optionalDependencies: (0, (_extends2 || _load_extends()).default)({}, workspaceManifestJson.optionalDependencies), + private: workspaceManifestJson.private, + workspaces: workspaceManifestJson.workspaces + }; + workspaceLayout.virtualManifestName = virtualDependencyManifest.name; + const virtualDep = {}; + virtualDep[virtualDependencyManifest.name] = virtualDependencyManifest.version; + workspaces[virtualDependencyManifest.name] = { loc: workspacesRoot, manifest: virtualDependencyManifest }; + + // ensure dependencies that should be excluded are stripped from the correct manifest + stripExcluded(cwdIsRoot ? virtualDependencyManifest : workspaces[projectManifestJson.name].manifest); + + pushDeps('workspaces', { workspaces: virtualDep }, { hint: 'workspaces', optional: false }, true); + + const implicitWorkspaceDependencies = (0, (_extends2 || _load_extends()).default)({}, workspaceDependencies); + + for (var _iterator6 = (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref6; + + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref6 = _iterator6[_i6++]; + } else { + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref6 = _i6.value; + } + + const type = _ref6; + + for (var _iterator7 = Object.keys(projectManifestJson[type] || {}), _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + var _ref7; + + if (_isArray7) { + if (_i7 >= _iterator7.length) break; + _ref7 = _iterator7[_i7++]; + } else { + _i7 = _iterator7.next(); + if (_i7.done) break; + _ref7 = _i7.value; + } + + const dependencyName = _ref7; + + delete implicitWorkspaceDependencies[dependencyName]; + } + } + + pushDeps('dependencies', { dependencies: implicitWorkspaceDependencies }, { hint: 'workspaces', optional: false }, true); + } + + break; + } + + // inherit root flat flag + if (manifest.flat) { + _this.flags.flat = true; + } + + return { + requests: [...resolutionDeps, ...deps], + patterns, + manifest, + usedPatterns, + ignorePatterns, + workspaceLayout + }; + })(); + } + + /** + * TODO description + */ + + prepareRequests(requests) { + return requests; + } + + preparePatterns(patterns) { + return patterns; + } + preparePatternsForLinking(patterns, cwdManifest, cwdIsRoot) { + return patterns; + } + + prepareManifests() { + var _this2 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const manifests = yield _this2.config.getRootManifests(); + return manifests; + })(); + } + + bailout(patterns, workspaceLayout) { + var _this3 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // We don't want to skip the audit - it could yield important errors + if (_this3.flags.audit) { + return false; + } + // PNP is so fast that the integrity check isn't pertinent + if (_this3.config.plugnplayEnabled) { + return false; + } + if (_this3.flags.skipIntegrityCheck || _this3.flags.force) { + return false; + } + const lockfileCache = _this3.lockfile.cache; + if (!lockfileCache) { + return false; + } + const lockfileClean = _this3.lockfile.parseResultType === 'success'; + const match = yield _this3.integrityChecker.check(patterns, lockfileCache, _this3.flags, workspaceLayout); + if (_this3.flags.frozenLockfile && (!lockfileClean || match.missingPatterns.length > 0)) { + throw new (_errors || _load_errors()).MessageError(_this3.reporter.lang('frozenLockfileError')); + } + + const haveLockfile = yield (_fs || _load_fs()).exists(path.join(_this3.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME)); + + const lockfileIntegrityPresent = !_this3.lockfile.hasEntriesExistWithoutIntegrity(); + const integrityBailout = lockfileIntegrityPresent || !_this3.config.autoAddIntegrity; + + if (match.integrityMatches && haveLockfile && lockfileClean && integrityBailout) { + _this3.reporter.success(_this3.reporter.lang('upToDate')); + return true; + } + + if (match.integrityFileMissing && haveLockfile) { + // Integrity file missing, force script installations + _this3.scripts.setForce(true); + return false; + } + + if (match.hardRefreshRequired) { + // e.g. node version doesn't match, force script installations + _this3.scripts.setForce(true); + return false; + } + + if (!patterns.length && !match.integrityFileMissing) { + _this3.reporter.success(_this3.reporter.lang('nothingToInstall')); + yield _this3.createEmptyManifestFolders(); + yield _this3.saveLockfileAndIntegrity(patterns, workspaceLayout); + return true; + } + + return false; + })(); + } + + /** + * Produce empty folders for all used root manifests. + */ + + createEmptyManifestFolders() { + var _this4 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + if (_this4.config.modulesFolder) { + // already created + return; + } + + for (var _iterator9 = _this4.rootManifestRegistries, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref10; + + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref10 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref10 = _i9.value; + } + + const registryName = _ref10; + const folder = _this4.config.registries[registryName].folder; + + yield (_fs || _load_fs()).mkdirp(path.join(_this4.config.lockfileFolder, folder)); + } + })(); + } + + /** + * TODO description + */ + + markIgnored(patterns) { + for (var _iterator10 = patterns, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref11; + + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref11 = _iterator10[_i10++]; + } else { + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref11 = _i10.value; + } + + const pattern = _ref11; + + const manifest = this.resolver.getStrictResolvedPattern(pattern); + const ref = manifest._reference; + invariant(ref, 'expected package reference'); + + // just mark the package as ignored. if the package is used by a required package, the hoister + // will take care of that. + ref.ignore = true; + } + } + + /** + * helper method that gets only recent manifests + * used by global.ls command + */ + getFlattenedDeps() { + var _this5 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + var _ref12 = yield _this5.fetchRequestFromCwd(); + + const depRequests = _ref12.requests, + rawPatterns = _ref12.patterns; + + + yield _this5.resolver.init(depRequests, {}); + + const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this5.resolver.getManifests(), _this5.config); + _this5.resolver.updateManifests(manifests); + + return _this5.flatten(rawPatterns); + })(); + } + + /** + * TODO description + */ + + init() { + var _this6 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + _this6.checkUpdate(); + + // warn if we have a shrinkwrap + if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_SHRINKWRAP_FILENAME))) { + _this6.reporter.warn(_this6.reporter.lang('shrinkwrapWarning')); + } + + // warn if we have an npm lockfile + if (yield (_fs || _load_fs()).exists(path.join(_this6.config.lockfileFolder, (_constants || _load_constants()).NPM_LOCK_FILENAME))) { + _this6.reporter.warn(_this6.reporter.lang('npmLockfileWarning')); + } + + if (_this6.config.plugnplayEnabled) { + _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L1')); + _this6.reporter.info(_this6.reporter.lang('plugnplaySuggestV2L2')); + } + + let flattenedTopLevelPatterns = []; + const steps = []; + + var _ref13 = yield _this6.fetchRequestFromCwd(); + + const depRequests = _ref13.requests, + rawPatterns = _ref13.patterns, + ignorePatterns = _ref13.ignorePatterns, + workspaceLayout = _ref13.workspaceLayout, + manifest = _ref13.manifest; + + let topLevelPatterns = []; + + const artifacts = yield _this6.integrityChecker.getArtifacts(); + if (artifacts) { + _this6.linker.setArtifacts(artifacts); + _this6.scripts.setArtifacts(artifacts); + } + + if ((_packageCompatibility || _load_packageCompatibility()).shouldCheck(manifest, _this6.flags)) { + steps.push((() => { + var _ref14 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { + _this6.reporter.step(curr, total, _this6.reporter.lang('checkingManifest'), emoji.get('mag')); + yield _this6.checkCompatibility(); + }); + + return function (_x, _x2) { + return _ref14.apply(this, arguments); + }; + })()); + } + + const audit = new (_audit || _load_audit()).default(_this6.config, _this6.reporter, { groups: (_constants || _load_constants()).OWNED_DEPENDENCY_TYPES }); + let auditFoundProblems = false; + + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('resolveStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + _this6.reporter.step(curr, total, _this6.reporter.lang('resolvingPackages'), emoji.get('mag')); + yield _this6.resolver.init(_this6.prepareRequests(depRequests), { + isFlat: _this6.flags.flat, + isFrozen: _this6.flags.frozenLockfile, + workspaceLayout + }); + topLevelPatterns = _this6.preparePatterns(rawPatterns); + flattenedTopLevelPatterns = yield _this6.flatten(topLevelPatterns); + return { bailout: !_this6.flags.audit && (yield _this6.bailout(topLevelPatterns, workspaceLayout)) }; + })); + }); + + if (_this6.flags.audit) { + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('auditStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + _this6.reporter.step(curr, total, _this6.reporter.lang('auditRunning'), emoji.get('mag')); + if (_this6.flags.offline) { + _this6.reporter.warn(_this6.reporter.lang('auditOffline')); + return { bailout: false }; + } + const preparedManifests = yield _this6.prepareManifests(); + // $FlowFixMe - Flow considers `m` in the map operation to be "mixed", so does not recognize `m.object` + const mergedManifest = Object.assign({}, ...Object.values(preparedManifests).map(function (m) { + return m.object; + })); + const auditVulnerabilityCounts = yield audit.performAudit(mergedManifest, _this6.lockfile, _this6.resolver, _this6.linker, topLevelPatterns); + auditFoundProblems = auditVulnerabilityCounts.info || auditVulnerabilityCounts.low || auditVulnerabilityCounts.moderate || auditVulnerabilityCounts.high || auditVulnerabilityCounts.critical; + return { bailout: yield _this6.bailout(topLevelPatterns, workspaceLayout) }; + })); + }); + } + + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('fetchStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + _this6.markIgnored(ignorePatterns); + _this6.reporter.step(curr, total, _this6.reporter.lang('fetchingPackages'), emoji.get('truck')); + const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this6.resolver.getManifests(), _this6.config); + _this6.resolver.updateManifests(manifests); + yield (_packageCompatibility || _load_packageCompatibility()).check(_this6.resolver.getManifests(), _this6.config, _this6.flags.ignoreEngines); + })); + }); + + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('linkStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // remove integrity hash to make this operation atomic + yield _this6.integrityChecker.removeIntegrityFile(); + _this6.reporter.step(curr, total, _this6.reporter.lang('linkingDependencies'), emoji.get('link')); + flattenedTopLevelPatterns = _this6.preparePatternsForLinking(flattenedTopLevelPatterns, manifest, _this6.config.lockfileFolder === _this6.config.cwd); + yield _this6.linker.init(flattenedTopLevelPatterns, workspaceLayout, { + linkDuplicates: _this6.flags.linkDuplicates, + ignoreOptional: _this6.flags.ignoreOptional + }); + })); + }); + + if (_this6.config.plugnplayEnabled) { + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('pnpStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const pnpPath = `${_this6.config.lockfileFolder}/${(_constants || _load_constants()).PNP_FILENAME}`; + + const code = yield (0, (_generatePnpMap || _load_generatePnpMap()).generatePnpMap)(_this6.config, flattenedTopLevelPatterns, { + resolver: _this6.resolver, + reporter: _this6.reporter, + targetPath: pnpPath, + workspaceLayout + }); + + try { + const file = yield (_fs || _load_fs()).readFile(pnpPath); + if (file === code) { + return; + } + } catch (error) {} + + yield (_fs || _load_fs()).writeFile(pnpPath, code); + yield (_fs || _load_fs()).chmod(pnpPath, 0o755); + })); + }); + } + + steps.push(function (curr, total) { + return (0, (_hooks || _load_hooks()).callThroughHook)('buildStep', (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + _this6.reporter.step(curr, total, _this6.flags.force ? _this6.reporter.lang('rebuildingPackages') : _this6.reporter.lang('buildingFreshPackages'), emoji.get('hammer')); + + if (_this6.config.ignoreScripts) { + _this6.reporter.warn(_this6.reporter.lang('ignoredScripts')); + } else { + yield _this6.scripts.init(flattenedTopLevelPatterns); + } + })); + }); + + if (_this6.flags.har) { + steps.push((() => { + var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { + const formattedDate = new Date().toISOString().replace(/:/g, '-'); + const filename = `yarn-install_${formattedDate}.har`; + _this6.reporter.step(curr, total, _this6.reporter.lang('savingHar', filename), emoji.get('black_circle_for_record')); + yield _this6.config.requestManager.saveHar(filename); + }); + + return function (_x3, _x4) { + return _ref21.apply(this, arguments); + }; + })()); + } + + if (yield _this6.shouldClean()) { + steps.push((() => { + var _ref22 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (curr, total) { + _this6.reporter.step(curr, total, _this6.reporter.lang('cleaningModules'), emoji.get('recycle')); + yield (0, (_autoclean || _load_autoclean()).clean)(_this6.config, _this6.reporter); + }); + + return function (_x5, _x6) { + return _ref22.apply(this, arguments); + }; + })()); + } + + let currentStep = 0; + for (var _iterator11 = steps, _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { + var _ref23; + + if (_isArray11) { + if (_i11 >= _iterator11.length) break; + _ref23 = _iterator11[_i11++]; + } else { + _i11 = _iterator11.next(); + if (_i11.done) break; + _ref23 = _i11.value; + } + + const step = _ref23; + + const stepResult = yield step(++currentStep, steps.length); + if (stepResult && stepResult.bailout) { + if (_this6.flags.audit) { + audit.summary(); + } + if (auditFoundProblems) { + _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); + } + _this6.maybeOutputUpdate(); + return flattenedTopLevelPatterns; + } + } + + // fin! + if (_this6.flags.audit) { + audit.summary(); + } + if (auditFoundProblems) { + _this6.reporter.warn(_this6.reporter.lang('auditRunAuditForDetails')); + } + yield _this6.saveLockfileAndIntegrity(topLevelPatterns, workspaceLayout); + yield _this6.persistChanges(); + _this6.maybeOutputUpdate(); + _this6.config.requestManager.clearCache(); + return flattenedTopLevelPatterns; + })(); + } + + checkCompatibility() { + var _this7 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + var _ref24 = yield _this7.fetchRequestFromCwd(); + + const manifest = _ref24.manifest; + + yield (_packageCompatibility || _load_packageCompatibility()).checkOne(manifest, _this7.config, _this7.flags.ignoreEngines); + })(); + } + + persistChanges() { + var _this8 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // get all the different registry manifests in this folder + const manifests = yield _this8.config.getRootManifests(); + + if (yield _this8.applyChanges(manifests)) { + yield _this8.config.saveRootManifests(manifests); + } + })(); + } + + applyChanges(manifests) { + let hasChanged = false; + + if (this.config.plugnplayPersist) { + const object = manifests.npm.object; + + + if (typeof object.installConfig !== 'object') { + object.installConfig = {}; + } + + if (this.config.plugnplayEnabled && object.installConfig.pnp !== true) { + object.installConfig.pnp = true; + hasChanged = true; + } else if (!this.config.plugnplayEnabled && typeof object.installConfig.pnp !== 'undefined') { + delete object.installConfig.pnp; + hasChanged = true; + } + + if (Object.keys(object.installConfig).length === 0) { + delete object.installConfig; + } + } + + return Promise.resolve(hasChanged); + } + + /** + * Check if we should run the cleaning step. + */ + + shouldClean() { + return (_fs || _load_fs()).exists(path.join(this.config.lockfileFolder, (_constants || _load_constants()).CLEAN_FILENAME)); + } + + /** + * TODO + */ + + flatten(patterns) { + var _this9 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + if (!_this9.flags.flat) { + return patterns; + } + + const flattenedPatterns = []; + + for (var _iterator12 = _this9.resolver.getAllDependencyNamesByLevelOrder(patterns), _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { + var _ref25; + + if (_isArray12) { + if (_i12 >= _iterator12.length) break; + _ref25 = _iterator12[_i12++]; + } else { + _i12 = _iterator12.next(); + if (_i12.done) break; + _ref25 = _i12.value; + } + + const name = _ref25; + + const infos = _this9.resolver.getAllInfoForPackageName(name).filter(function (manifest) { + const ref = manifest._reference; + invariant(ref, 'expected package reference'); + return !ref.ignore; + }); + + if (infos.length === 0) { + continue; + } + + if (infos.length === 1) { + // single version of this package + // take out a single pattern as multiple patterns may have resolved to this package + flattenedPatterns.push(_this9.resolver.patternsByPackage[name][0]); + continue; + } + + const options = infos.map(function (info) { + const ref = info._reference; + invariant(ref, 'expected reference'); + return { + // TODO `and is required by {PARENT}`, + name: _this9.reporter.lang('manualVersionResolutionOption', ref.patterns.join(', '), info.version), + + value: info.version + }; + }); + const versions = infos.map(function (info) { + return info.version; + }); + let version; + + const resolutionVersion = _this9.resolutions[name]; + if (resolutionVersion && versions.indexOf(resolutionVersion) >= 0) { + // use json `resolution` version + version = resolutionVersion; + } else { + version = yield _this9.reporter.select(_this9.reporter.lang('manualVersionResolution', name), _this9.reporter.lang('answer'), options); + _this9.resolutions[name] = version; + } + + flattenedPatterns.push(_this9.resolver.collapseAllVersionsOfPackage(name, version)); + } + + // save resolutions to their appropriate root manifest + if (Object.keys(_this9.resolutions).length) { + const manifests = yield _this9.config.getRootManifests(); + + for (const name in _this9.resolutions) { + const version = _this9.resolutions[name]; + + const patterns = _this9.resolver.patternsByPackage[name]; + if (!patterns) { + continue; + } + + let manifest; + for (var _iterator13 = patterns, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { + var _ref26; + + if (_isArray13) { + if (_i13 >= _iterator13.length) break; + _ref26 = _iterator13[_i13++]; + } else { + _i13 = _iterator13.next(); + if (_i13.done) break; + _ref26 = _i13.value; + } + + const pattern = _ref26; + + manifest = _this9.resolver.getResolvedPattern(pattern); + if (manifest) { + break; + } + } + invariant(manifest, 'expected manifest'); + + const ref = manifest._reference; + invariant(ref, 'expected reference'); + + const object = manifests[ref.registry].object; + object.resolutions = object.resolutions || {}; + object.resolutions[name] = version; + } + + yield _this9.config.saveRootManifests(manifests); + } + + return flattenedPatterns; + })(); + } + + /** + * Remove offline tarballs that are no longer required + */ + + pruneOfflineMirror(lockfile) { + var _this10 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const mirror = _this10.config.getOfflineMirrorPath(); + if (!mirror) { + return; + } + + const requiredTarballs = new Set(); + for (const dependency in lockfile) { + const resolved = lockfile[dependency].resolved; + if (resolved) { + const basename = path.basename(resolved.split('#')[0]); + if (dependency[0] === '@' && basename[0] !== '@') { + requiredTarballs.add(`${dependency.split('/')[0]}-${basename}`); + } + requiredTarballs.add(basename); + } + } + + const mirrorFiles = yield (_fs || _load_fs()).walk(mirror); + for (var _iterator14 = mirrorFiles, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { + var _ref27; + + if (_isArray14) { + if (_i14 >= _iterator14.length) break; + _ref27 = _iterator14[_i14++]; + } else { + _i14 = _iterator14.next(); + if (_i14.done) break; + _ref27 = _i14.value; + } + + const file = _ref27; + + const isTarball = path.extname(file.basename) === '.tgz'; + // if using experimental-pack-script-packages-in-mirror flag, don't unlink prebuilt packages + const hasPrebuiltPackage = file.relative.startsWith('prebuilt/'); + if (isTarball && !hasPrebuiltPackage && !requiredTarballs.has(file.basename)) { + yield (_fs || _load_fs()).unlink(file.absolute); + } + } + })(); + } + + /** + * Save updated integrity and lockfiles. + */ + + saveLockfileAndIntegrity(patterns, workspaceLayout) { + var _this11 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const resolvedPatterns = {}; + Object.keys(_this11.resolver.patterns).forEach(function (pattern) { + if (!workspaceLayout || !workspaceLayout.getManifestByPattern(pattern)) { + resolvedPatterns[pattern] = _this11.resolver.patterns[pattern]; + } + }); + + // TODO this code is duplicated in a few places, need a common way to filter out workspace patterns from lockfile + patterns = patterns.filter(function (p) { + return !workspaceLayout || !workspaceLayout.getManifestByPattern(p); + }); + + const lockfileBasedOnResolver = _this11.lockfile.getLockfile(resolvedPatterns); + + if (_this11.config.pruneOfflineMirror) { + yield _this11.pruneOfflineMirror(lockfileBasedOnResolver); + } + + // write integrity hash + if (!_this11.config.plugnplayEnabled) { + yield _this11.integrityChecker.save(patterns, lockfileBasedOnResolver, _this11.flags, workspaceLayout, _this11.scripts.getArtifacts()); + } + + // --no-lockfile or --pure-lockfile or --frozen-lockfile + if (_this11.flags.lockfile === false || _this11.flags.pureLockfile || _this11.flags.frozenLockfile) { + return; + } + + const lockFileHasAllPatterns = patterns.every(function (p) { + return _this11.lockfile.getLocked(p); + }); + const lockfilePatternsMatch = Object.keys(_this11.lockfile.cache || {}).every(function (p) { + return lockfileBasedOnResolver[p]; + }); + const resolverPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { + const manifest = _this11.lockfile.getLocked(pattern); + return manifest && manifest.resolved === lockfileBasedOnResolver[pattern].resolved && deepEqual(manifest.prebuiltVariants, lockfileBasedOnResolver[pattern].prebuiltVariants); + }); + const integrityPatternsAreSameAsInLockfile = Object.keys(lockfileBasedOnResolver).every(function (pattern) { + const existingIntegrityInfo = lockfileBasedOnResolver[pattern].integrity; + if (!existingIntegrityInfo) { + // if this entry does not have an integrity, no need to re-write the lockfile because of it + return true; + } + const manifest = _this11.lockfile.getLocked(pattern); + if (manifest && manifest.integrity) { + const manifestIntegrity = ssri.stringify(manifest.integrity); + return manifestIntegrity === existingIntegrityInfo; + } + return false; + }); + + // remove command is followed by install with force, lockfile will be rewritten in any case then + if (!_this11.flags.force && _this11.lockfile.parseResultType === 'success' && lockFileHasAllPatterns && lockfilePatternsMatch && resolverPatternsAreSameAsInLockfile && integrityPatternsAreSameAsInLockfile && patterns.length) { + return; + } + + // build lockfile location + const loc = path.join(_this11.config.lockfileFolder, (_constants || _load_constants()).LOCKFILE_FILENAME); + + // write lockfile + const lockSource = (0, (_lockfile2 || _load_lockfile2()).stringify)(lockfileBasedOnResolver, false, _this11.config.enableLockfileVersions); + yield (_fs || _load_fs()).writeFilePreservingEol(loc, lockSource); + + _this11._logSuccessSaveLockfile(); + })(); + } + + _logSuccessSaveLockfile() { + this.reporter.success(this.reporter.lang('savedLockfile')); + } + + /** + * Load the dependency graph of the current install. Only does package resolving and wont write to the cwd. + */ + hydrate(ignoreUnusedPatterns) { + var _this12 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + const request = yield _this12.fetchRequestFromCwd([], ignoreUnusedPatterns); + const depRequests = request.requests, + rawPatterns = request.patterns, + ignorePatterns = request.ignorePatterns, + workspaceLayout = request.workspaceLayout; + + + yield _this12.resolver.init(depRequests, { + isFlat: _this12.flags.flat, + isFrozen: _this12.flags.frozenLockfile, + workspaceLayout + }); + yield _this12.flatten(rawPatterns); + _this12.markIgnored(ignorePatterns); + + // fetch packages, should hit cache most of the time + const manifests = yield (_packageFetcher || _load_packageFetcher()).fetch(_this12.resolver.getManifests(), _this12.config); + _this12.resolver.updateManifests(manifests); + yield (_packageCompatibility || _load_packageCompatibility()).check(_this12.resolver.getManifests(), _this12.config, _this12.flags.ignoreEngines); + + // expand minimal manifests + for (var _iterator15 = _this12.resolver.getManifests(), _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { + var _ref28; + + if (_isArray15) { + if (_i15 >= _iterator15.length) break; + _ref28 = _iterator15[_i15++]; + } else { + _i15 = _iterator15.next(); + if (_i15.done) break; + _ref28 = _i15.value; + } + + const manifest = _ref28; + + const ref = manifest._reference; + invariant(ref, 'expected reference'); + const type = ref.remote.type; + // link specifier won't ever hit cache + + let loc = ''; + if (type === 'link') { + continue; + } else if (type === 'workspace') { + if (!ref.remote.reference) { + continue; + } + loc = ref.remote.reference; + } else { + loc = _this12.config.generateModuleCachePath(ref); + } + const newPkg = yield _this12.config.readManifest(loc); + yield _this12.resolver.updateManifest(ref, newPkg); + } + + return request; + })(); + } + + /** + * Check for updates every day and output a nag message if there's a newer version. + */ + + checkUpdate() { + if (this.config.nonInteractive) { + // don't show upgrade dialog on CI or non-TTY terminals + return; + } + + // don't check if disabled + if (this.config.getOption('disable-self-update-check')) { + return; + } + + // only check for updates once a day + const lastUpdateCheck = Number(this.config.getOption('lastUpdateCheck')) || 0; + if (lastUpdateCheck && Date.now() - lastUpdateCheck < ONE_DAY) { + return; + } + + // don't bug for updates on tagged releases + if ((_yarnVersion || _load_yarnVersion()).version.indexOf('-') >= 0) { + return; + } + + this._checkUpdate().catch(() => { + // swallow errors + }); + } + + _checkUpdate() { + var _this13 = this; + + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + let latestVersion = yield _this13.config.requestManager.request({ + url: (_constants || _load_constants()).SELF_UPDATE_VERSION_URL + }); + invariant(typeof latestVersion === 'string', 'expected string'); + latestVersion = latestVersion.trim(); + if (!semver.valid(latestVersion)) { + return; + } + + // ensure we only check for updates periodically + _this13.config.registries.yarn.saveHomeConfig({ + lastUpdateCheck: Date.now() + }); + + if (semver.gt(latestVersion, (_yarnVersion || _load_yarnVersion()).version)) { + const installationMethod = yield (0, (_yarnVersion || _load_yarnVersion()).getInstallationMethod)(); + _this13.maybeOutputUpdate = function () { + _this13.reporter.warn(_this13.reporter.lang('yarnOutdated', latestVersion, (_yarnVersion || _load_yarnVersion()).version)); + + const command = getUpdateCommand(installationMethod); + if (command) { + _this13.reporter.info(_this13.reporter.lang('yarnOutdatedCommand')); + _this13.reporter.command(command); + } else { + const installer = getUpdateInstaller(installationMethod); + if (installer) { + _this13.reporter.info(_this13.reporter.lang('yarnOutdatedInstaller', installer)); + } + } + }; + } + })(); + } + + /** + * Method to override with a possible upgrade message. + */ + + maybeOutputUpdate() {} +} + +exports.Install = Install; +function hasWrapper(commander, args) { + return true; +} + +function setFlags(commander) { + commander.description('Yarn install is used to install all dependencies for a project.'); + commander.usage('install [flags]'); + commander.option('-A, --audit', 'Run vulnerability audit on installed packages'); + commander.option('-g, --global', 'DEPRECATED'); + commander.option('-S, --save', 'DEPRECATED - save package to your `dependencies`'); + commander.option('-D, --save-dev', 'DEPRECATED - save package to your `devDependencies`'); + commander.option('-P, --save-peer', 'DEPRECATED - save package to your `peerDependencies`'); + commander.option('-O, --save-optional', 'DEPRECATED - save package to your `optionalDependencies`'); + commander.option('-E, --save-exact', 'DEPRECATED'); + commander.option('-T, --save-tilde', 'DEPRECATED'); +} + +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + +var isObject = __webpack_require__(52); +module.exports = function (it) { + if (!isObject(it)) throw TypeError(it + ' is not an object!'); + return it; +}; + + +/***/ }), +/* 36 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return SubjectSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Subject; }); +/* unused harmony export AnonymousSubject */ +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_tslib__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Observable__ = __webpack_require__(12); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Subscriber__ = __webpack_require__(7); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Subscription__ = __webpack_require__(25); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__ = __webpack_require__(189); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__ = __webpack_require__(422); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__ = __webpack_require__(321); +/** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ + + + + + + + +var SubjectSubscriber = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](SubjectSubscriber, _super); + function SubjectSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.destination = destination; + return _this; + } + return SubjectSubscriber; +}(__WEBPACK_IMPORTED_MODULE_2__Subscriber__["a" /* Subscriber */])); + +var Subject = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](Subject, _super); + function Subject() { + var _this = _super.call(this) || this; + _this.observers = []; + _this.closed = false; + _this.isStopped = false; + _this.hasError = false; + _this.thrownError = null; + return _this; + } + Subject.prototype[__WEBPACK_IMPORTED_MODULE_6__internal_symbol_rxSubscriber__["a" /* rxSubscriber */]] = function () { + return new SubjectSubscriber(this); + }; + Subject.prototype.lift = function (operator) { + var subject = new AnonymousSubject(this, this); + subject.operator = operator; + return subject; + }; + Subject.prototype.next = function (value) { + if (this.closed) { + throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); + } + if (!this.isStopped) { + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].next(value); + } + } + }; + Subject.prototype.error = function (err) { + if (this.closed) { + throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); + } + this.hasError = true; + this.thrownError = err; + this.isStopped = true; + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].error(err); + } + this.observers.length = 0; + }; + Subject.prototype.complete = function () { + if (this.closed) { + throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); + } + this.isStopped = true; + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].complete(); + } + this.observers.length = 0; + }; + Subject.prototype.unsubscribe = function () { + this.isStopped = true; + this.closed = true; + this.observers = null; + }; + Subject.prototype._trySubscribe = function (subscriber) { + if (this.closed) { + throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); + } + else { + return _super.prototype._trySubscribe.call(this, subscriber); + } + }; + Subject.prototype._subscribe = function (subscriber) { + if (this.closed) { + throw new __WEBPACK_IMPORTED_MODULE_4__util_ObjectUnsubscribedError__["a" /* ObjectUnsubscribedError */](); + } + else if (this.hasError) { + subscriber.error(this.thrownError); + return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; + } + else if (this.isStopped) { + subscriber.complete(); + return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; + } + else { + this.observers.push(subscriber); + return new __WEBPACK_IMPORTED_MODULE_5__SubjectSubscription__["a" /* SubjectSubscription */](this, subscriber); + } + }; + Subject.prototype.asObservable = function () { + var observable = new __WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */](); + observable.source = this; + return observable; + }; + Subject.create = function (destination, source) { + return new AnonymousSubject(destination, source); + }; + return Subject; +}(__WEBPACK_IMPORTED_MODULE_1__Observable__["a" /* Observable */])); + +var AnonymousSubject = /*@__PURE__*/ (function (_super) { + __WEBPACK_IMPORTED_MODULE_0_tslib__["a" /* __extends */](AnonymousSubject, _super); + function AnonymousSubject(destination, source) { + var _this = _super.call(this) || this; + _this.destination = destination; + _this.source = source; + return _this; + } + AnonymousSubject.prototype.next = function (value) { + var destination = this.destination; + if (destination && destination.next) { + destination.next(value); + } + }; + AnonymousSubject.prototype.error = function (err) { + var destination = this.destination; + if (destination && destination.error) { + this.destination.error(err); + } + }; + AnonymousSubject.prototype.complete = function () { + var destination = this.destination; + if (destination && destination.complete) { + this.destination.complete(); + } + }; + AnonymousSubject.prototype._subscribe = function (subscriber) { + var source = this.source; + if (source) { + return this.source.subscribe(subscriber); + } + else { + return __WEBPACK_IMPORTED_MODULE_3__Subscription__["a" /* Subscription */].EMPTY; + } + }; + return AnonymousSubject; +}(Subject)); + +//# sourceMappingURL=Subject.js.map + + +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.normalizePattern = normalizePattern; + +/** + * Explode and normalize a pattern into its name and range. + */ + +function normalizePattern(pattern) { + let hasVersion = false; + let range = 'latest'; + let name = pattern; + + // if we're a scope then remove the @ and add it back later + let isScoped = false; + if (name[0] === '@') { + isScoped = true; + name = name.slice(1); + } + + // take first part as the name + const parts = name.split('@'); + if (parts.length > 1) { + name = parts.shift(); + range = parts.join('@'); + + if (range) { + hasVersion = true; + } else { + range = '*'; + } + } + + // add back @ scope suffix + if (isScoped) { + name = `@${name}`; + } + + return { name, range, hasVersion }; +} + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_RESULT__;/** + * @license + * Lodash + * Copyright JS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.17.10'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Error message constants. */ + var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', + FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the maximum memoize cache size. */ + var MAX_MEMOIZE_SIZE = 500; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** Used to compose bitmasks for cloning. */ + var CLONE_DEEP_FLAG = 1, + CLONE_FLAT_FLAG = 2, + CLONE_SYMBOLS_FLAG = 4; + + /** Used to compose bitmasks for value comparisons. */ + var COMPARE_PARTIAL_FLAG = 1, + COMPARE_UNORDERED_FLAG = 2; + + /** Used to compose bitmasks for function metadata. */ + var WRAP_BIND_FLAG = 1, + WRAP_BIND_KEY_FLAG = 2, + WRAP_CURRY_BOUND_FLAG = 4, + WRAP_CURRY_FLAG = 8, + WRAP_CURRY_RIGHT_FLAG = 16, + WRAP_PARTIAL_FLAG = 32, + WRAP_PARTIAL_RIGHT_FLAG = 64, + WRAP_ARY_FLAG = 128, + WRAP_REARG_FLAG = 256, + WRAP_FLIP_FLAG = 512; + + /** Used as default options for `_.truncate`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2, + LAZY_WHILE_FLAG = 3; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** Used to associate wrap methods with their bit flags. */ + var wrapFlags = [ + ['ary', WRAP_ARY_FLAG], + ['bind', WRAP_BIND_FLAG], + ['bindKey', WRAP_BIND_KEY_FLAG], + ['curry', WRAP_CURRY_FLAG], + ['curryRight', WRAP_CURRY_RIGHT_FLAG], + ['flip', WRAP_FLIP_FLAG], + ['partial', WRAP_PARTIAL_FLAG], + ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], + ['rearg', WRAP_REARG_FLAG] + ]; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + asyncTag = '[object AsyncFunction]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + domExcTag = '[object DOMException]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + nullTag = '[object Null]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + proxyTag = '[object Proxy]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + undefinedTag = '[object Undefined]', + weakMapTag = '[object WeakMap]', + weakSetTag = '[object WeakSet]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, + reUnescapedHtml = /[&<>"']/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/g, + reTrimStart = /^\s+/, + reTrimEnd = /\s+$/; + + /** Used to match wrap detail comments. */ + var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, + reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, + reSplitDetails = /,? & /; + + /** Used to match words composed of alphanumeric characters. */ + var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** + * Used to match + * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). + */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, + rsDingbatRange = '\\u2700-\\u27bf', + rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', + rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', + rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', + rsPunctuationRange = '\\u2000-\\u206f', + rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', + rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', + rsVarRange = '\\ufe0e\\ufe0f', + rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]", + rsAstral = '[' + rsAstralRange + ']', + rsBreak = '[' + rsBreakRange + ']', + rsCombo = '[' + rsComboRange + ']', + rsDigits = '\\d+', + rsDingbat = '[' + rsDingbatRange + ']', + rsLower = '[' + rsLowerRange + ']', + rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsUpper = '[' + rsUpperRange + ']', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', + rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', + rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', + rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', + reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])', + rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, + rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; + + /** Used to match apostrophes. */ + var reApos = RegExp(rsApos, 'g'); + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ + var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); + + /** Used to match complex or compound words. */ + var reUnicodeWord = RegExp([ + rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', + rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', + rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, + rsUpper + '+' + rsOptContrUpper, + rsOrdUpper, + rsOrdLower, + rsDigits, + rsEmoji + ].join('|'), 'g'); + + /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ + var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); + + /** Used to detect strings that need a more robust regexp to match words. */ + var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', + 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', + '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Built-in method references without a dependency on `root`. */ + var freeParseFloat = parseFloat, + freeParseInt = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + // Use `util.types` for Node.js 10+. + var types = freeModule && freeModule.require && freeModule.require('util').types; + + if (types) { + return types; + } + + // Legacy `process.binding('util')` for Node.js < 10. + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, + nodeIsDate = nodeUtil && nodeUtil.isDate, + nodeIsMap = nodeUtil && nodeUtil.isMap, + nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, + nodeIsSet = nodeUtil && nodeUtil.isSet, + nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /*--------------------------------------------------------------------------*/ + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `baseAggregator` for arrays. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function arrayAggregator(array, setter, iteratee, accumulator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + var value = array[index]; + setter(accumulator, value, iteratee(value), array); + } + return accumulator; + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array == null ? 0 : array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludes(array, value) { + var length = array == null ? 0 : array.length; + return !!length && baseIndexOf(array, value, 0) > -1; + } + + /** + * This function is like `arrayIncludes` except that it accepts a comparator. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (comparator(value, array[index])) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array == null ? 0 : array.length; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the last element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initAccum) { + var length = array == null ? 0 : array.length; + if (initAccum && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * Gets the size of an ASCII `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + var asciiSize = baseProperty('length'); + + /** + * Converts an ASCII `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function asciiToArray(string) { + return string.split(''); + } + + /** + * Splits an ASCII `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function asciiWords(string) { + return string.match(reAsciiWord) || []; + } + + /** + * The base implementation of methods like `_.findKey` and `_.findLastKey`, + * without support for iteratee shorthands, which iterates over `collection` + * using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFindKey(collection, predicate, eachFunc) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = key; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + return value === value + ? strictIndexOf(array, value, fromIndex) + : baseFindIndex(array, baseIsNaN, fromIndex); + } + + /** + * This function is like `baseIndexOf` except that it accepts a comparator. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @param {Function} comparator The comparator invoked per element. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOfWith(array, value, fromIndex, comparator) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (comparator(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ + function baseIsNaN(value) { + return value !== value; + } + + /** + * The base implementation of `_.mean` and `_.meanBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the mean. + */ + function baseMean(array, iteratee) { + var length = array == null ? 0 : array.length; + return length ? (baseSum(array, iteratee) / length) : NAN; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.propertyOf` without support for deep paths. + * + * @private + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyOf(object) { + return function(key) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight`, without support + * for iteratee shorthands, which iterates over `collection` using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initAccum Specify using the first or last element of + * `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initAccum + ? (initAccum = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define the + * sort order of `array` and replaces criteria objects with their corresponding + * values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sum` and `_.sumBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(array, iteratee) { + var result, + index = -1, + length = array.length; + + while (++index < length) { + var current = iteratee(array[index]); + if (current !== undefined) { + result = result === undefined ? current : (result + current); + } + } + return result; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. + */ + function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); + } + + /** + * Checks if a `cache` value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the first unmatched string symbol. + */ + function charsStartIndex(strSymbols, chrSymbols) { + var index = -1, + length = strSymbols.length; + + while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the last unmatched string symbol. + */ + function charsEndIndex(strSymbols, chrSymbols) { + var index = strSymbols.length; + + while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Gets the number of `placeholder` occurrences in `array`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} placeholder The placeholder to search for. + * @returns {number} Returns the placeholder count. + */ + function countHolders(array, placeholder) { + var length = array.length, + result = 0; + + while (length--) { + if (array[length] === placeholder) { + ++result; + } + } + return result; + } + + /** + * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A + * letters to basic Latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + var deburrLetter = basePropertyOf(deburredLetters); + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + var escapeHtmlChar = basePropertyOf(htmlEscapes); + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `string` contains Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a symbol is found, else `false`. + */ + function hasUnicode(string) { + return reHasUnicode.test(string); + } + + /** + * Checks if `string` contains a word composed of Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a word is found, else `false`. + */ + function hasUnicodeWord(string) { + return reHasUnicodeWord.test(string); + } + + /** + * Converts `iterator` to an array. + * + * @private + * @param {Object} iterator The iterator to convert. + * @returns {Array} Returns the converted array. + */ + function iteratorToArray(iterator) { + var data, + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value === placeholder || value === PLACEHOLDER) { + array[index] = PLACEHOLDER; + result[resIndex++] = index; + } + } + return result; + } + + /** + * Gets the value at `key`, unless `key` is "__proto__". + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function safeGet(object, key) { + return key == '__proto__' + ? undefined + : object[key]; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** + * Converts `set` to its value-value pairs. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the value-value pairs. + */ + function setToPairs(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = [value, value]; + }); + return result; + } + + /** + * A specialized version of `_.indexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictIndexOf(array, value, fromIndex) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * A specialized version of `_.lastIndexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictLastIndexOf(array, value, fromIndex) { + var index = fromIndex + 1; + while (index--) { + if (array[index] === value) { + return index; + } + } + return index; + } + + /** + * Gets the number of symbols in `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the string size. + */ + function stringSize(string) { + return hasUnicode(string) + ? unicodeSize(string) + : asciiSize(string); + } + + /** + * Converts `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function stringToArray(string) { + return hasUnicode(string) + ? unicodeToArray(string) + : asciiToArray(string); + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + var unescapeHtmlChar = basePropertyOf(htmlUnescapes); + + /** + * Gets the size of a Unicode `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + function unicodeSize(string) { + var result = reUnicode.lastIndex = 0; + while (reUnicode.test(string)) { + ++result; + } + return result; + } + + /** + * Converts a Unicode `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function unicodeToArray(string) { + return string.match(reUnicode) || []; + } + + /** + * Splits a Unicode `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function unicodeWords(string) { + return string.match(reUnicodeWord) || []; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the `context` object. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Util + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // Create a suped-up `defer` in Node.js. + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + var runInContext = (function runInContext(context) { + context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); + + /** Built-in constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = context['__core-js_shared__']; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? context.Buffer : undefined, + Symbol = context.Symbol, + Uint8Array = context.Uint8Array, + allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, + symIterator = Symbol ? Symbol.iterator : undefined, + symToStringTag = Symbol ? Symbol.toStringTag : undefined; + + var defineProperty = (function() { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }()); + + /** Mocked built-ins. */ + var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, + ctxNow = Date && Date.now !== root.Date.now && Date.now, + ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeFloor = Math.floor, + nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeIsFinite = context.isFinite, + nativeJoin = arrayProto.join, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = Date.now, + nativeParseInt = context.parseInt, + nativeRandom = Math.random, + nativeReverse = arrayProto.reverse; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(context, 'DataView'), + Map = getNative(context, 'Map'), + Promise = getNative(context, 'Promise'), + Set = getNative(context, 'Set'), + WeakMap = getNative(context, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array and iteratees accept only + * one argument. The heuristic for whether a section qualifies for shortcut + * fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, + * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, + * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, + * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, + * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, + * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, + * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, + * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, + * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, + * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, + * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, + * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, + * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, + * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, + * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, + * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, + * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, + * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, + * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, + * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, + * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, + * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, + * `upperFirst`, `value`, and `words` + * + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2, 3]); + * + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 + * + * // Returns a wrapped value. + * var squares = wrapped.map(square); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} proto The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(proto) { + if (!isObject(proto)) { + return {}; + } + if (objectCreate) { + return objectCreate(proto); + } + object.prototype = proto; + var result = new object; + object.prototype = undefined; + return result; + }; + }()); + + /** + * The function whose prototype chain sequence wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable explicit method chain sequences. + */ + function LodashWrapper(value, chainAll) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; + } + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB) as well as ES2015 template strings. Change the + * following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type {Object} + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type {string} + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type {Object} + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type {Function} + */ + '_': lodash + } + }; + + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + lodash.prototype.constructor = lodash; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @constructor + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = MAX_ARRAY_LENGTH; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = copyArray(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = copyArray(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = copyArray(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || (!isRight && arrLength == length && takeCount == length)) { + return baseWrapperValue(array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + // Ensure `LazyWrapper` is an instance of `baseLodash`. + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.size = 0; + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + var result = getMapData(this, key)['delete'](key); + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + var data = getMapData(this, key), + size = data.size; + + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values == null ? 0 : values.length; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + var data = this.__data__, + result = data['delete'](key); + + this.size = data.size; + return result; + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && ( + // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || + // Node.js 0.10 has enumerable non-index properties on buffers. + (isBuff && (key == 'offset' || key == 'parent')) || + // PhantomJS 2 has enumerable non-index properties on typed arrays. + (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || + // Skip index properties. + isIndex(key, length) + ))) { + result.push(key); + } + } + return result; + } + + /** + * A specialized version of `_.sample` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @returns {*} Returns the random element. + */ + function arraySample(array) { + var length = array.length; + return length ? array[baseRandom(0, length - 1)] : undefined; + } + + /** + * A specialized version of `_.sampleSize` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function arraySampleSize(array, n) { + return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); + } + + /** + * A specialized version of `_.shuffle` for arrays. + * + * @private + * @param {Array} array The array to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function arrayShuffle(array) { + return shuffleSelf(copyArray(array)); + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * Aggregates elements of `collection` on `accumulator` with keys transformed + * by `iteratee` and values set by `setter`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function baseAggregator(collection, setter, iteratee, accumulator) { + baseEach(collection, function(value, key, collection) { + setter(accumulator, value, iteratee(value), collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.assignIn` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssignIn(object, source) { + return object && copyObject(source, keysIn(source), object); + } + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); + } else { + object[key] = value; + } + } + + /** + * The base implementation of `_.at` without support for individual paths. + * + * @private + * @param {Object} object The object to iterate over. + * @param {string[]} paths The property paths to pick. + * @returns {Array} Returns the picked elements. + */ + function baseAt(object, paths) { + var index = -1, + length = paths.length, + result = Array(length), + skip = object == null; + + while (++index < length) { + result[index] = skip ? undefined : get(object, paths[index]); + } + return result; + } + + /** + * The base implementation of `_.clamp` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + */ + function baseClamp(number, lower, upper) { + if (number === number) { + if (upper !== undefined) { + number = number <= upper ? number : upper; + } + if (lower !== undefined) { + number = number >= lower ? number : lower; + } + } + return number; + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, bitmask, customizer, key, object, stack) { + var result, + isDeep = bitmask & CLONE_DEEP_FLAG, + isFlat = bitmask & CLONE_FLAT_FLAG, + isFull = bitmask & CLONE_SYMBOLS_FLAG; + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value); + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, baseAssignIn(result, value)) + : copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + if (isSet(value)) { + value.forEach(function(subValue) { + result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)); + }); + + return result; + } + + if (isMap(value)) { + value.forEach(function(subValue, key) { + result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + + return result; + } + + var keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys); + + var props = isArr ? undefined : keysFunc(value); + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.conforms` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + */ + function baseConforms(source) { + var props = keys(source); + return function(object) { + return baseConformsTo(object, source, props); + }; + } + + /** + * The base implementation of `_.conformsTo` which accepts `props` to check. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + */ + function baseConformsTo(object, source, props) { + var length = props.length; + if (object == null) { + return !length; + } + object = Object(object); + while (length--) { + var key = props[length], + predicate = source[key], + value = object[key]; + + if ((value === undefined && !(key in object)) || !predicate(value)) { + return false; + } + } + return true; + } + + /** + * The base implementation of `_.delay` and `_.defer` which accepts `args` + * to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Array} args The arguments to provide to `func`. + * @returns {number|Object} Returns the timer id or timeout object. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of methods like `_.difference` without support + * for excluding multiple arrays or iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + isCommon = true, + length = array.length, + result = [], + valuesLength = values.length; + + if (!length) { + return result; + } + if (iteratee) { + values = arrayMap(values, baseUnary(iteratee)); + } + if (comparator) { + includes = arrayIncludesWith; + isCommon = false; + } + else if (values.length >= LARGE_ARRAY_SIZE) { + includes = cacheHas; + isCommon = false; + values = new SetCache(values); + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee == null ? value : iteratee(value); + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === computed) { + continue outer; + } + } + result.push(value); + } + else if (!includes(values, computed, comparator)) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * The base implementation of methods like `_.max` and `_.min` which accepts a + * `comparator` to determine the extremum value. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per iteration. + * @param {Function} comparator The comparator used to compare values. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(array, iteratee, comparator) { + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index], + current = iteratee(value); + + if (current != null && (computed === undefined + ? (current === current && !isSymbol(current)) + : comparator(current, computed) + )) { + var computed = current, + result = value; + } + } + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = toInteger(start); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : toInteger(end); + if (end < 0) { + end += length; + } + end = start > end ? 0 : toLength(end); + while (start < end) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with support for restricting flattening. + * + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, depth, predicate, isStrict, result) { + var index = -1, + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); + + while (++index < length) { + var value = array[index]; + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return object && baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from `props`. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the function names. + */ + function baseFunctions(object, props) { + return arrayFilter(props, function(key) { + return isFunction(object[key]); + }); + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = castPath(path, object); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); + } + + /** + * The base implementation of `_.gt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + */ + function baseGt(value, other) { + return value > other; + } + + /** + * The base implementation of `_.has` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHas(object, key) { + return object != null && hasOwnProperty.call(object, key); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.inRange` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to check. + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + */ + function baseInRange(number, start, end) { + return number >= nativeMin(start, end) && number < nativeMax(start, end); + } + + /** + * The base implementation of methods like `_.intersection`, without support + * for iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of shared values. + */ + function baseIntersection(arrays, iteratee, comparator) { + var includes = comparator ? arrayIncludesWith : arrayIncludes, + length = arrays[0].length, + othLength = arrays.length, + othIndex = othLength, + caches = Array(othLength), + maxLength = Infinity, + result = []; + + while (othIndex--) { + var array = arrays[othIndex]; + if (othIndex && iteratee) { + array = arrayMap(array, baseUnary(iteratee)); + } + maxLength = nativeMin(array.length, maxLength); + caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) + ? new SetCache(othIndex && array) + : undefined; + } + array = arrays[0]; + + var index = -1, + seen = caches[0]; + + outer: + while (++index < length && result.length < maxLength) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (!(seen + ? cacheHas(seen, computed) + : includes(result, computed, comparator) + )) { + othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if (!(cache + ? cacheHas(cache, computed) + : includes(arrays[othIndex], computed, comparator)) + ) { + continue outer; + } + } + if (seen) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.invert` and `_.invertBy` which inverts + * `object` with values transformed by `iteratee` and set by `setter`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform values. + * @param {Object} accumulator The initial inverted object. + * @returns {Function} Returns `accumulator`. + */ + function baseInverter(object, setter, iteratee, accumulator) { + baseForOwn(object, function(value, key, object) { + setter(accumulator, iteratee(value), key, object); + }); + return accumulator; + } + + /** + * The base implementation of `_.invoke` without support for individual + * method arguments. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function baseInvoke(object, path, args) { + path = castPath(path, object); + object = parent(object, path); + var func = object == null ? object : object[toKey(last(path))]; + return func == null ? undefined : apply(func, object, args); + } + + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + + /** + * The base implementation of `_.isArrayBuffer` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + */ + function baseIsArrayBuffer(value) { + return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; + } + + /** + * The base implementation of `_.isDate` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + */ + function baseIsDate(value) { + return isObjectLike(value) && baseGetTag(value) == dateTag; + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {boolean} bitmask The bitmask flags. + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Function} [customizer] The function to customize comparisons. + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = objIsArr ? arrayTag : getTag(object), + othTag = othIsArr ? arrayTag : getTag(other); + + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + + /** + * The base implementation of `_.isMap` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + */ + function baseIsMap(value) { + return isObjectLike(value) && getTag(value) == mapTag; + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isRegExp` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + */ + function baseIsRegExp(value) { + return isObjectLike(value) && baseGetTag(value) == regexpTag; + } + + /** + * The base implementation of `_.isSet` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + */ + function baseIsSet(value) { + return isObjectLike(value) && getTag(value) == setTag; + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.lt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + */ + function baseLt(value, other) { + return value < other; + } + + /** + * The base implementation of `_.map` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + baseFor(source, function(srcValue, key) { + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }, keysIn); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = safeGet(object, key), + srcValue = safeGet(source, key), + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + var isArr = isArray(srcValue), + isBuff = !isArr && isBuffer(srcValue), + isTyped = !isArr && !isBuff && isTypedArray(srcValue); + + newValue = srcValue; + if (isArr || isBuff || isTyped) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else if (isBuff) { + isCommon = false; + newValue = cloneBuffer(srcValue, true); + } + else if (isTyped) { + isCommon = false; + newValue = cloneTypedArray(srcValue, true); + } + else { + newValue = []; + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + newValue = objValue; + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + newValue = initCloneObject(srcValue); + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.nth` which doesn't coerce arguments. + * + * @private + * @param {Array} array The array to query. + * @param {number} n The index of the element to return. + * @returns {*} Returns the nth element of `array`. + */ + function baseNth(array, n) { + var length = array.length; + if (!length) { + return; + } + n += n < 0 ? length : 0; + return isIndex(n, length) ? array[n] : undefined; + } + + /** + * The base implementation of `_.orderBy` without param guards. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {string[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseOrderBy(collection, iteratees, orders) { + var index = -1; + iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee())); + + var result = baseMap(collection, function(value, key, collection) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.pick` without support for individual + * property identifiers. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @returns {Object} Returns the new object. + */ + function basePick(object, paths) { + return basePickBy(object, paths, function(value, path) { + return hasIn(object, path); + }); + } + + /** + * The base implementation of `_.pickBy` without support for iteratee shorthands. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @param {Function} predicate The function invoked per property. + * @returns {Object} Returns the new object. + */ + function basePickBy(object, paths, predicate) { + var index = -1, + length = paths.length, + result = {}; + + while (++index < length) { + var path = paths[index], + value = baseGet(object, path); + + if (predicate(value, path)) { + baseSet(result, castPath(path, object), value); + } + } + return result; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.pullAllBy` without support for iteratee + * shorthands. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + */ + function basePullAll(array, values, iteratee, comparator) { + var indexOf = comparator ? baseIndexOfWith : baseIndexOf, + index = -1, + length = values.length, + seen = array; + + if (array === values) { + values = copyArray(values); + } + if (iteratee) { + seen = arrayMap(array, baseUnary(iteratee)); + } + while (++index < length) { + var fromIndex = 0, + value = values[index], + computed = iteratee ? iteratee(value) : value; + + while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { + if (seen !== array) { + splice.call(seen, fromIndex, 1); + } + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * indexes or capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0, + lastIndex = length - 1; + + while (length--) { + var index = indexes[length]; + if (length == lastIndex || index !== previous) { + var previous = index; + if (isIndex(index)) { + splice.call(array, index, 1); + } else { + baseUnset(array, index); + } + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for returning + * floating-point numbers. + * + * @private + * @param {number} lower The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the random number. + */ + function baseRandom(lower, upper) { + return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); + } + + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; + } + return result; + } + + /** + * The base implementation of `_.repeat` which doesn't coerce arguments. + * + * @private + * @param {string} string The string to repeat. + * @param {number} n The number of times to repeat the string. + * @returns {string} Returns the repeated string. + */ + function baseRepeat(string, n) { + var result = ''; + if (!string || n < 1 || n > MAX_SAFE_INTEGER) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + if (n) { + string += string; + } + } while (n); + + return result; + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); + } + + /** + * The base implementation of `_.sample`. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + */ + function baseSample(collection) { + return arraySample(values(collection)); + } + + /** + * The base implementation of `_.sampleSize` without param guards. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function baseSampleSize(collection, n) { + var array = values(collection); + return shuffleSelf(array, baseClamp(n, 0, array.length)); + } + + /** + * The base implementation of `_.set`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseSet(object, path, value, customizer) { + if (!isObject(object)) { + return object; + } + path = castPath(path, object); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = toKey(path[index]), + newValue = value; + + if (index != lastIndex) { + var objValue = nested[key]; + newValue = customizer ? customizer(objValue, key, nested) : undefined; + if (newValue === undefined) { + newValue = isObject(objValue) + ? objValue + : (isIndex(path[index + 1]) ? [] : {}); + } + } + assignValue(nested, key, newValue); + nested = nested[key]; + } + return object; + } + + /** + * The base implementation of `setData` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var baseSetToString = !defineProperty ? identity : function(func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; + + /** + * The base implementation of `_.shuffle`. + * + * @private + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function baseShuffle(collection) { + return shuffleSelf(values(collection)); + } + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which + * performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndex(array, value, retHighest) { + var low = 0, + high = array == null ? low : array.length; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if (computed !== null && !isSymbol(computed) && + (retHighest ? (computed <= value) : (computed < value))) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return baseSortedIndexBy(array, value, identity, retHighest); + } + + /** + * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` + * which invokes `iteratee` for `value` and each element of `array` to compute + * their sort ranking. The iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndexBy(array, value, iteratee, retHighest) { + value = iteratee(value); + + var low = 0, + high = array == null ? 0 : array.length, + valIsNaN = value !== value, + valIsNull = value === null, + valIsSymbol = isSymbol(value), + valIsUndefined = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + othIsDefined = computed !== undefined, + othIsNull = computed === null, + othIsReflexive = computed === computed, + othIsSymbol = isSymbol(computed); + + if (valIsNaN) { + var setLow = retHighest || othIsReflexive; + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined); + } else if (valIsNull) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); + } else if (othIsNull || othIsSymbol) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseSortedUniq(array, iteratee) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + if (!index || !eq(computed, seen)) { + var seen = computed; + result[resIndex++] = value === 0 ? 0 : value; + } + } + return result; + } + + /** + * The base implementation of `_.toNumber` which doesn't ensure correct + * conversions of binary, hexadecimal, or octal string values. + * + * @private + * @param {*} value The value to process. + * @returns {number} Returns the number. + */ + function baseToNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + return +value; + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return arrayMap(value, baseToString) + ''; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * The base implementation of `_.uniqBy` without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; + + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); + } + isCommon = false; + includes = cacheHas; + seen = new SetCache; + } + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.unset`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The property path to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + */ + function baseUnset(object, path) { + path = castPath(path, object); + object = parent(object, path); + return object == null || delete object[toKey(last(path))]; + } + + /** + * The base implementation of `_.update`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to update. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseUpdate(object, path, updater, customizer) { + return baseSet(object, path, updater(baseGet(object, path)), customizer); + } + + /** + * The base implementation of methods like `_.dropWhile` and `_.takeWhile` + * without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && + predicate(array[index], index, array)) {} + + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to perform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + return arrayReduce(actions, function(result, action) { + return action.func.apply(action.thisArg, arrayPush([result], action.args)); + }, result); + } + + /** + * The base implementation of methods like `_.xor`, without support for + * iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of values. + */ + function baseXor(arrays, iteratee, comparator) { + var length = arrays.length; + if (length < 2) { + return length ? baseUniq(arrays[0]) : []; + } + var index = -1, + result = Array(length); + + while (++index < length) { + var array = arrays[index], + othIndex = -1; + + while (++othIndex < length) { + if (othIndex != index) { + result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); + } + } + } + return baseUniq(baseFlatten(result, 1), iteratee, comparator); + } + + /** + * This base implementation of `_.zipObject` which assigns values using `assignFunc`. + * + * @private + * @param {Array} props The property identifiers. + * @param {Array} values The property values. + * @param {Function} assignFunc The function to assign values. + * @returns {Object} Returns the new object. + */ + function baseZipObject(props, values, assignFunc) { + var index = -1, + length = props.length, + valsLength = values.length, + result = {}; + + while (++index < length) { + var value = index < valsLength ? values[index] : undefined; + assignFunc(result, props[index], value); + } + return result; + } + + /** + * Casts `value` to an empty array if it's not an array like object. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array|Object} Returns the cast array-like object. + */ + function castArrayLikeObject(value) { + return isArrayLikeObject(value) ? value : []; + } + + /** + * Casts `value` to `identity` if it's not a function. + * + * @private + * @param {*} value The value to inspect. + * @returns {Function} Returns cast function. + */ + function castFunction(value) { + return typeof value == 'function' ? value : identity; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @param {Object} [object] The object to query keys on. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + + /** + * A `baseRest` alias which can be replaced with `identity` by module + * replacement plugins. + * + * @private + * @type {Function} + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + var castRest = baseRest; + + /** + * Casts `array` to a slice if it's needed. + * + * @private + * @param {Array} array The array to inspect. + * @param {number} start The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the cast slice. + */ + function castSlice(array, start, end) { + var length = array.length; + end = end === undefined ? length : end; + return (!start && end >= length) ? array : baseSlice(array, start, end); + } + + /** + * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). + * + * @private + * @param {number|Object} id The timer id or timeout object of the timer to clear. + */ + var clearTimeout = ctxClearTimeout || function(id) { + return root.clearTimeout(id); + }; + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var length = buffer.length, + result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); + + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, + valIsNull = value === null, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsDefined = other !== undefined, + othIsNull = other === null, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || + (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || + (valIsNull && othIsDefined && othIsReflexive) || + (!valIsDefined && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || + (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || + (othIsNull && valIsDefined && valIsReflexive) || + (!othIsDefined && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; + } + + /** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersLength = holders.length, + leftIndex = -1, + leftLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(leftLength + rangeLength), + isUncurried = !isCurried; + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + } + while (rangeLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersIndex = -1, + holdersLength = holders.length, + rightIndex = -1, + rightLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(rangeLength + rightLength), + isUncurried = !isCurried; + + while (++argsIndex < rangeLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + if (newValue === undefined) { + newValue = source[key]; + } + if (isNew) { + baseAssignValue(object, key, newValue); + } else { + assignValue(object, key, newValue); + } + } + return object; + } + + /** + * Copies own symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Copies own and inherited symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbolsIn(source, object) { + return copyObject(source, getSymbolsIn(source), object); + } + + /** + * Creates a function like `_.groupBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} [initializer] The accumulator object initializer. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee) { + var func = isArray(collection) ? arrayAggregator : baseAggregator, + accumulator = initializer ? initializer() : {}; + + return func(collection, setter, getIteratee(iteratee, 2), accumulator); + }; + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` to invoke it with the optional `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createBind(func, bitmask, thisArg) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, arguments); + } + return wrapper; + } + + /** + * Creates a function like `_.lowerFirst`. + * + * @private + * @param {string} methodName The name of the `String` case method to use. + * @returns {Function} Returns the new case function. + */ + function createCaseFirst(methodName) { + return function(string) { + string = toString(string); + + var strSymbols = hasUnicode(string) + ? stringToArray(string) + : undefined; + + var chr = strSymbols + ? strSymbols[0] + : string.charAt(0); + + var trailing = strSymbols + ? castSlice(strSymbols, 1).join('') + : string.slice(1); + + return chr[methodName]() + trailing; + }; + } + + /** + * Creates a function like `_.camelCase`. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtor(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. See + // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a function that wraps `func` to enable currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {number} arity The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createCurry(func, bitmask, arity) { + var Ctor = createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length, + placeholder = getHolder(wrapper); + + while (index--) { + args[index] = arguments[index]; + } + var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) + ? [] + : replaceHolders(args, placeholder); + + length -= holders.length; + if (length < arity) { + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, undefined, + args, holders, undefined, undefined, arity - length); + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return apply(fn, this, args); + } + return wrapper; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} findIndexFunc The function to find the collection index. + * @returns {Function} Returns the new find function. + */ + function createFind(findIndexFunc) { + return function(collection, predicate, fromIndex) { + var iterable = Object(collection); + if (!isArrayLike(collection)) { + var iteratee = getIteratee(predicate, 3); + collection = keys(collection); + predicate = function(key) { return iteratee(iterable[key], key, iterable); }; + } + var index = findIndexFunc(collection, predicate, fromIndex); + return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return flatRest(function(funcs) { + var length = funcs.length, + index = length, + prereq = LodashWrapper.prototype.thru; + + if (fromRight) { + funcs.reverse(); + } + while (index--) { + var func = funcs[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (prereq && !wrapper && getFuncName(func) == 'wrapper') { + var wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? index : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && + data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && + !data[4].length && data[9] == 1 + ) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) + ? wrapper[funcName]() + : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value)) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }); + } + + /** + * Creates a function that wraps `func` to invoke it with optional `this` + * binding of `thisArg`, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided + * to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & WRAP_ARY_FLAG, + isBind = bitmask & WRAP_BIND_FLAG, + isBindKey = bitmask & WRAP_BIND_KEY_FLAG, + isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), + isFlip = bitmask & WRAP_FLIP_FLAG, + Ctor = isBindKey ? undefined : createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length; + + while (index--) { + args[index] = arguments[index]; + } + if (isCurried) { + var placeholder = getHolder(wrapper), + holdersCount = countHolders(args, placeholder); + } + if (partials) { + args = composeArgs(args, partials, holders, isCurried); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight, isCurried); + } + length -= holdersCount; + if (isCurried && length < arity) { + var newHolders = replaceHolders(args, placeholder); + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, thisArg, + args, newHolders, argPos, ary, arity - length + ); + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + length = args.length; + if (argPos) { + args = reorder(args, argPos); + } else if (isFlip && length > 1) { + args.reverse(); + } + if (isAry && ary < length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtor(fn); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates a function like `_.invertBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} toIteratee The function to resolve iteratees. + * @returns {Function} Returns the new inverter function. + */ + function createInverter(setter, toIteratee) { + return function(object, iteratee) { + return baseInverter(object, setter, toIteratee(iteratee), {}); + }; + } + + /** + * Creates a function that performs a mathematical operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @param {number} [defaultValue] The value used for `undefined` arguments. + * @returns {Function} Returns the new mathematical operation function. + */ + function createMathOperation(operator, defaultValue) { + return function(value, other) { + var result; + if (value === undefined && other === undefined) { + return defaultValue; + } + if (value !== undefined) { + result = value; + } + if (other !== undefined) { + if (result === undefined) { + return other; + } + if (typeof value == 'string' || typeof other == 'string') { + value = baseToString(value); + other = baseToString(other); + } else { + value = baseToNumber(value); + other = baseToNumber(other); + } + result = operator(value, other); + } + return result; + }; + } + + /** + * Creates a function like `_.over`. + * + * @private + * @param {Function} arrayFunc The function to iterate over iteratees. + * @returns {Function} Returns the new over function. + */ + function createOver(arrayFunc) { + return flatRest(function(iteratees) { + iteratees = arrayMap(iteratees, baseUnary(getIteratee())); + return baseRest(function(args) { + var thisArg = this; + return arrayFunc(iteratees, function(iteratee) { + return apply(iteratee, thisArg, args); + }); + }); + }); + } + + /** + * Creates the padding for `string` based on `length`. The `chars` string + * is truncated if the number of characters exceeds `length`. + * + * @private + * @param {number} length The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padding for `string`. + */ + function createPadding(length, chars) { + chars = chars === undefined ? ' ' : baseToString(chars); + + var charsLength = chars.length; + if (charsLength < 2) { + return charsLength ? baseRepeat(chars, length) : chars; + } + var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); + return hasUnicode(chars) + ? castSlice(stringToArray(result), 0, length).join('') + : result.slice(0, length); + } + + /** + * Creates a function that wraps `func` to invoke it with the `this` binding + * of `thisArg` and `partials` prepended to the arguments it receives. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to + * the new function. + * @returns {Function} Returns the new wrapped function. + */ + function createPartial(func, bitmask, thisArg, partials) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength), + fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + return apply(fn, isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ + function createRange(fromRight) { + return function(start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } + // Ensure the sign of `-0` is preserved. + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); + return baseRange(start, end, step, fromRight); + }; + } + + /** + * Creates a function that performs a relational operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new relational operation function. + */ + function createRelationalOperation(operator) { + return function(value, other) { + if (!(typeof value == 'string' && typeof other == 'string')) { + value = toNumber(value); + other = toNumber(other); + } + return operator(value, other); + }; + } + + /** + * Creates a function that wraps `func` to continue currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {Function} wrapFunc The function to create the `func` wrapper. + * @param {*} placeholder The placeholder value. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { + var isCurry = bitmask & WRAP_CURRY_FLAG, + newHolders = isCurry ? holders : undefined, + newHoldersRight = isCurry ? undefined : holders, + newPartials = isCurry ? partials : undefined, + newPartialsRight = isCurry ? undefined : partials; + + bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); + + if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { + bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); + } + var newData = [ + func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, + newHoldersRight, argPos, ary, arity + ]; + + var result = wrapFunc.apply(undefined, newData); + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return setWrapToString(result, func, bitmask); + } + + /** + * Creates a function like `_.round`. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + number = toNumber(number); + precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); + if (precision) { + // Shift with exponential notation to avoid floating-point issues. + // See [MDN](https://mdn.io/round#Examples) for more details. + var pair = (toString(number) + 'e').split('e'), + value = func(pair[0] + 'e' + (+pair[1] + precision)); + + pair = (toString(value) + 'e').split('e'); + return +(pair[0] + 'e' + (+pair[1] - precision)); + } + return func(number); + }; + } + + /** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ + var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { + return new Set(values); + }; + + /** + * Creates a `_.toPairs` or `_.toPairsIn` function. + * + * @private + * @param {Function} keysFunc The function to get the keys of a given object. + * @returns {Function} Returns the new pairs function. + */ + function createToPairs(keysFunc) { + return function(object) { + var tag = getTag(object); + if (tag == mapTag) { + return mapToArray(object); + } + if (tag == setTag) { + return setToPairs(object); + } + return baseToPairs(object, keysFunc(object)); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * 512 - `_.flip` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); + arity = arity === undefined ? arity : toInteger(arity); + length -= holders ? holders.length : 0; + + if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func); + + var newData = [ + func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, + argPos, ary, arity + ]; + + if (data) { + mergeData(newData, data); + } + func = newData[0]; + bitmask = newData[1]; + thisArg = newData[2]; + partials = newData[3]; + holders = newData[4]; + arity = newData[9] = newData[9] === undefined + ? (isBindKey ? 0 : func.length) + : nativeMax(newData[9] - length, 0); + + if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { + bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); + } + if (!bitmask || bitmask == WRAP_BIND_FLAG) { + var result = createBind(func, bitmask, thisArg); + } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { + result = createCurry(func, bitmask, arity); + } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { + result = createPartial(func, bitmask, thisArg, partials); + } else { + result = createHybrid.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setWrapToString(setter(result, newData), func, bitmask); + } + + /** + * Used by `_.defaults` to customize its `_.assignIn` use to assign properties + * of source objects to the destination object for all destination properties + * that resolve to `undefined`. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to assign. + * @param {Object} object The parent object of `objValue`. + * @returns {*} Returns the value to assign. + */ + function customDefaultsAssignIn(objValue, srcValue, key, object) { + if (objValue === undefined || + (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { + return srcValue; + } + return objValue; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source + * objects into destination objects that are passed thru. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to merge. + * @param {Object} object The parent object of `objValue`. + * @param {Object} source The parent object of `srcValue`. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + * @returns {*} Returns the value to assign. + */ + function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { + if (isObject(objValue) && isObject(srcValue)) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, objValue); + baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); + stack['delete'](srcValue); + } + return objValue; + } + + /** + * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain + * objects. + * + * @private + * @param {*} value The value to inspect. + * @param {string} key The key of the property to inspect. + * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. + */ + function customOmitClone(value) { + return isPlainObject(value) ? undefined : value; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + objProps = getAllKeys(object), + objLength = objProps.length, + othProps = getAllKeys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseRest` which flattens the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + function flatRest(func) { + return setToString(overRest(func, undefined, flatten), func + ''); + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Creates an array of own and inherited enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeysIn(object) { + return baseGetAllKeys(object, keysIn, getSymbolsIn); + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = (func.name + ''), + array = realNames[result], + length = hasOwnProperty.call(realNames, result) ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the argument placeholder value for `func`. + * + * @private + * @param {Function} func The function to inspect. + * @returns {*} Returns the placeholder value. + */ + function getHolder(func) { + var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; + return object.placeholder; + } + + /** + * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, + * this function returns the custom method, otherwise it returns `baseIteratee`. + * If arguments are provided, the chosen function is invoked with them and + * its result is returned. + * + * @private + * @param {*} [value] The value to convert to an iteratee. + * @param {number} [arity] The arity of the created iteratee. + * @returns {Function} Returns the chosen function or its result. + */ + function getIteratee() { + var result = lodash.iteratee || iteratee; + result = result === iteratee ? baseIteratee : result; + return arguments.length ? result(arguments[0], arguments[1]) : result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + + /** + * Creates an array of the own enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + + /** + * Creates an array of the own and inherited enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { + var result = []; + while (object) { + arrayPush(result, getSymbols(object)); + object = getPrototype(object); + } + return result; + }; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = baseGetTag(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : ''; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Extracts wrapper details from the `source` body comment. + * + * @private + * @param {string} source The source to inspect. + * @returns {Array} Returns the wrapper details. + */ + function getWrapDetails(source) { + var match = source.match(reWrapDetails); + return match ? match[1].split(reSplitDetails) : []; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + + var index = -1, + length = path.length, + result = false; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = new array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return new Ctor; + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return new Ctor; + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Inserts wrapper `details` in a comment at the top of the `source` body. + * + * @private + * @param {string} source The source to modify. + * @returns {Array} details The details to insert. + * @returns {string} Returns the modified source. + */ + function insertWrapDetails(source, details) { + var length = details.length; + if (!length) { + return source; + } + var lastIndex = length - 1; + details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; + details = details.join(length > 2 ? ', ' : ' '); + return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); + } + + /** + * Checks if `value` is a flattenable `arguments` object or array. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. + */ + function isFlattenable(value) { + return isArray(value) || isArguments(value) || + !!(spreadableSymbol && value && value[spreadableSymbol]); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + var type = typeof value; + length = length == null ? MAX_SAFE_INTEGER : length; + + return !!length && + (type == 'number' || + (type != 'symbol' && reIsUint.test(value))) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, + * else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func), + other = lodash[funcName]; + + if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { + return false; + } + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `func` is capable of being masked. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `func` is maskable, else `false`. + */ + var isMaskable = coreJsData ? isFunction : stubFalse; + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * A specialized version of `_.memoize` which clears the memoized function's + * cache when it exceeds `MAX_MEMOIZE_SIZE`. + * + * @private + * @param {Function} func The function to have its output memoized. + * @returns {Function} Returns the new memoized function. + */ + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + + var cache = result.cache; + return result; + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers used to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and + * `_.rearg` modify function arguments, making the order in which they are + * executed important, preventing the merging of metadata. However, we make + * an exception for a safe combined case where curried functions have `_.ary` + * and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); + + var isCombo = + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || + ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & WRAP_BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : value; + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = value; + } + // Use source `ary` if it's smaller. + if (srcBitmask & WRAP_ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } + + /** + * Gets the parent value at `path` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path to get the parent value of. + * @returns {*} Returns the parent value. + */ + function parent(object, path) { + return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = copyArray(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity + * function to avoid garbage collection pauses in V8. See + * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = shortOut(baseSetData); + + /** + * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @returns {number|Object} Returns the timer id or timeout object. + */ + var setTimeout = ctxSetTimeout || function(func, wait) { + return root.setTimeout(func, wait); + }; + + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var setToString = shortOut(baseSetToString); + + /** + * Sets the `toString` method of `wrapper` to mimic the source of `reference` + * with wrapper details in a comment at the top of the source body. + * + * @private + * @param {Function} wrapper The function to modify. + * @param {Function} reference The reference function. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Function} Returns `wrapper`. + */ + function setWrapToString(wrapper, reference, bitmask) { + var source = (reference + ''); + return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); + } + + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ + function shortOut(func) { + var count = 0, + lastCalled = 0; + + return function() { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; + } + return func.apply(undefined, arguments); + }; + } + + /** + * A specialized version of `_.shuffle` which mutates and sets the size of `array`. + * + * @private + * @param {Array} array The array to shuffle. + * @param {number} [size=array.length] The size of `array`. + * @returns {Array} Returns `array`. + */ + function shuffleSelf(array, size) { + var index = -1, + length = array.length, + lastIndex = length - 1; + + size = size === undefined ? length : size; + while (++index < size) { + var rand = baseRandom(index, lastIndex), + value = array[rand]; + + array[rand] = array[index]; + array[index] = value; + } + array.length = size; + return array; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (string.charCodeAt(0) === 46 /* . */) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, subString) { + result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Updates wrapper `details` based on `bitmask` flags. + * + * @private + * @returns {Array} details The details to modify. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Array} Returns `details`. + */ + function updateWrapDetails(details, bitmask) { + arrayEach(wrapFlags, function(pair) { + var value = '_.' + pair[0]; + if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { + details.push(value); + } + }); + return details.sort(); + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + if (wrapper instanceof LazyWrapper) { + return wrapper.clone(); + } + var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); + result.__actions__ = copyArray(wrapper.__actions__); + result.__index__ = wrapper.__index__; + result.__values__ = wrapper.__values__; + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `array` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the new array of chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { + size = 1; + } else { + size = nativeMax(toInteger(size), 0); + } + var length = array == null ? 0 : array.length; + if (!length || size < 1) { + return []; + } + var index = 0, + resIndex = 0, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[resIndex++] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * Creates a new array concatenating `array` with any additional arrays + * and/or values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to concatenate. + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var other = _.concat(array, 2, [3], [[4]]); + * + * console.log(other); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + function concat() { + var length = arguments.length; + if (!length) { + return []; + } + var args = Array(length - 1), + array = arguments[0], + index = length; + + while (index--) { + args[index - 1] = arguments[index]; + } + return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); + } + + /** + * Creates an array of `array` values not included in the other given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * **Note:** Unlike `_.pullAll`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.without, _.xor + * @example + * + * _.difference([2, 1], [2, 3]); + * // => [1] + */ + var difference = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `iteratee` which + * is invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * **Note:** Unlike `_.pullAllBy`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2] + * + * // The `_.property` iteratee shorthand. + * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var differenceBy = baseRest(function(array, values) { + var iteratee = last(values); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `comparator` + * which is invoked to compare elements of `array` to `values`. The order and + * references of result values are determined by the first array. The comparator + * is invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.pullAllWith`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * + * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); + * // => [{ 'x': 2, 'y': 1 }] + */ + var differenceWith = baseRest(function(array, values) { + var comparator = last(values); + if (isArrayLikeObject(comparator)) { + comparator = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.dropRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney'] + * + * // The `_.matches` iteratee shorthand. + * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropRightWhile(users, ['active', false]); + * // => objects for ['barney'] + * + * // The `_.property` iteratee shorthand. + * _.dropRightWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.dropWhile(users, function(o) { return !o.active; }); + * // => objects for ['pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.dropWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropWhile(users, ['active', false]); + * // => objects for ['pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.dropWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8, 10], '*', 1, 3); + * // => [4, '*', '*', 10] + */ + function fill(array, value, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(o) { return o.user == 'barney'; }); + * // => 0 + * + * // The `_.matches` iteratee shorthand. + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findIndex(users, ['active', false]); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.findIndex(users, 'active'); + * // => 2 + */ + function findIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseFindIndex(array, getIteratee(predicate, 3), index); + } + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); + * // => 2 + * + * // The `_.matches` iteratee shorthand. + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastIndex(users, ['active', false]); + * // => 2 + * + * // The `_.property` iteratee shorthand. + * _.findLastIndex(users, 'active'); + * // => 0 + */ + function findLastIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length - 1; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = fromIndex < 0 + ? nativeMax(length + index, 0) + : nativeMin(index, length - 1); + } + return baseFindIndex(array, getIteratee(predicate, 3), index, true); + } + + /** + * Flattens `array` a single level deep. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, [3, [4]], 5]]); + * // => [1, 2, [3, [4]], 5] + */ + function flatten(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, 1) : []; + } + + /** + * Recursively flattens `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, [3, [4]], 5]]); + * // => [1, 2, 3, 4, 5] + */ + function flattenDeep(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, INFINITY) : []; + } + + /** + * Recursively flatten `array` up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Array + * @param {Array} array The array to flatten. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * var array = [1, [2, [3, [4]], 5]]; + * + * _.flattenDepth(array, 1); + * // => [1, 2, [3, [4]], 5] + * + * _.flattenDepth(array, 2); + * // => [1, 2, 3, [4], 5] + */ + function flattenDepth(array, depth) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(array, depth); + } + + /** + * The inverse of `_.toPairs`; this method returns an object composed + * from key-value `pairs`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} pairs The key-value pairs. + * @returns {Object} Returns the new object. + * @example + * + * _.fromPairs([['a', 1], ['b', 2]]); + * // => { 'a': 1, 'b': 2 } + */ + function fromPairs(pairs) { + var index = -1, + length = pairs == null ? 0 : pairs.length, + result = {}; + + while (++index < length) { + var pair = pairs[index]; + result[pair[0]] = pair[1]; + } + return result; + } + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias first + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.head([1, 2, 3]); + * // => 1 + * + * _.head([]); + * // => undefined + */ + function head(array) { + return (array && array.length) ? array[0] : undefined; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the + * offset from the end of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // Search from the `fromIndex`. + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + */ + function indexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseIndexOf(array, value, index); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 0, -1) : []; + } + + /** + * Creates an array of unique values that are included in all given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersection([2, 1], [2, 3]); + * // => [2] + */ + var intersection = baseRest(function(arrays) { + var mapped = arrayMap(arrays, castArrayLikeObject); + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `iteratee` + * which is invoked for each element of each `arrays` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [2.1] + * + * // The `_.property` iteratee shorthand. + * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }] + */ + var intersectionBy = baseRest(function(arrays) { + var iteratee = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + if (iteratee === last(mapped)) { + iteratee = undefined; + } else { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `comparator` + * which is invoked to compare elements of `arrays`. The order and references + * of result values are determined by the first array. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.intersectionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }] + */ + var intersectionWith = baseRest(function(arrays) { + var comparator = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + comparator = typeof comparator == 'function' ? comparator : undefined; + if (comparator) { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, undefined, comparator) + : []; + }); + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + function join(array, separator) { + return array == null ? '' : nativeJoin.call(array, separator); + } + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array == null ? 0 : array.length; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // Search from the `fromIndex`. + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); + } + return value === value + ? strictLastIndexOf(array, value, index) + : baseFindIndex(array, baseIsNaN, index, true); + } + + /** + * Gets the element at index `n` of `array`. If `n` is negative, the nth + * element from the end is returned. + * + * @static + * @memberOf _ + * @since 4.11.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=0] The index of the element to return. + * @returns {*} Returns the nth element of `array`. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * + * _.nth(array, 1); + * // => 'b' + * + * _.nth(array, -2); + * // => 'c'; + */ + function nth(array, n) { + return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; + } + + /** + * Removes all given values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` + * to remove elements from an array by predicate. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pull(array, 'a', 'c'); + * console.log(array); + * // => ['b', 'b'] + */ + var pull = baseRest(pullAll); + + /** + * This method is like `_.pull` except that it accepts an array of values to remove. + * + * **Note:** Unlike `_.difference`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pullAll(array, ['a', 'c']); + * console.log(array); + * // => ['b', 'b'] + */ + function pullAll(array, values) { + return (array && array.length && values && values.length) + ? basePullAll(array, values) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `iteratee` which is + * invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The iteratee is invoked with one argument: (value). + * + * **Note:** Unlike `_.differenceBy`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; + * + * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); + * console.log(array); + * // => [{ 'x': 2 }] + */ + function pullAllBy(array, values, iteratee) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, getIteratee(iteratee, 2)) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `comparator` which + * is invoked to compare elements of `array` to `values`. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.differenceWith`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; + * + * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); + * console.log(array); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] + */ + function pullAllWith(array, values, comparator) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, undefined, comparator) + : array; + } + + /** + * Removes elements from `array` corresponding to `indexes` and returns an + * array of removed elements. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * var pulled = _.pullAt(array, [1, 3]); + * + * console.log(array); + * // => ['a', 'c'] + * + * console.log(pulled); + * // => ['b', 'd'] + */ + var pullAt = flatRest(function(array, indexes) { + var length = array == null ? 0 : array.length, + result = baseAt(array, indexes); + + basePullAt(array, arrayMap(indexes, function(index) { + return isIndex(index, length) ? +index : index; + }).sort(compareAscending)); + + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is invoked + * with three arguments: (value, index, array). + * + * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` + * to pull elements from an array by value. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getIteratee(predicate, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Reverses `array` so that the first element becomes the last, the second + * element becomes the second to last, and so on. + * + * **Note:** This method mutates `array` and is based on + * [`Array#reverse`](https://mdn.io/Array/reverse). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.reverse(array); + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function reverse(array) { + return array == null ? array : nativeReverse.call(array); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of + * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are + * returned. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + else { + start = start == null ? 0 : toInteger(start); + end = end === undefined ? length : toInteger(end); + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + */ + function sortedIndex(array, value) { + return baseSortedIndex(array, value); + } + + /** + * This method is like `_.sortedIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); + * // => 0 + */ + function sortedIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); + } + + /** + * This method is like `_.indexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedIndexOf([4, 5, 5, 5, 6], 5); + * // => 1 + */ + function sortedIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value); + if (index < length && eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 5, 5, 5, 6], 5); + * // => 4 + */ + function sortedLastIndex(array, value) { + return baseSortedIndex(array, value, true); + } + + /** + * This method is like `_.sortedLastIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 1 + * + * // The `_.property` iteratee shorthand. + * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); + * // => 1 + */ + function sortedLastIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); + } + + /** + * This method is like `_.lastIndexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); + * // => 3 + */ + function sortedLastIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value, true) - 1; + if (eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.uniq` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniq([1, 1, 2]); + * // => [1, 2] + */ + function sortedUniq(array) { + return (array && array.length) + ? baseSortedUniq(array) + : []; + } + + /** + * This method is like `_.uniqBy` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); + * // => [1.1, 2.3] + */ + function sortedUniqBy(array, iteratee) { + return (array && array.length) + ? baseSortedUniq(array, getIteratee(iteratee, 2)) + : []; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.tail([1, 2, 3]); + * // => [2, 3] + */ + function tail(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 1, length) : []; + } + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + if (!(array && array.length)) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.takeRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeRightWhile(users, ['active', false]); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.takeRightWhile(users, 'active'); + * // => [] + */ + function takeRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.takeWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matches` iteratee shorthand. + * _.takeWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeWhile(users, ['active', false]); + * // => objects for ['barney', 'fred'] + * + * // The `_.property` iteratee shorthand. + * _.takeWhile(users, 'active'); + * // => [] + */ + function takeWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all given arrays using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([2], [1, 2]); + * // => [2, 1] + */ + var union = baseRest(function(arrays) { + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); + }); + + /** + * This method is like `_.union` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which uniqueness is computed. Result values are chosen from the first + * array in which the value occurs. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.unionBy([2.1], [1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + var unionBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.union` except that it accepts `comparator` which + * is invoked to compare elements of `arrays`. Result values are chosen from + * the first array in which the value occurs. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.unionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var unionWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurrence of each element + * is kept. The order of result values is determined by the order they occur + * in the array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + */ + function uniq(array) { + return (array && array.length) ? baseUniq(array) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The order of result values is determined by the + * order they occur in the array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniqBy([2.1, 1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniqBy(array, iteratee) { + return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `comparator` which + * is invoked to compare elements of `array`. The order of result values is + * determined by the order they occur in the array.The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.uniqWith(objects, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + */ + function uniqWith(array, comparator) { + comparator = typeof comparator == 'function' ? comparator : undefined; + return (array && array.length) ? baseUniq(array, undefined, comparator) : []; + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @since 1.2.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + * + * _.unzip(zipped); + * // => [['a', 'b'], [1, 2], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var length = 0; + array = arrayFilter(array, function(group) { + if (isArrayLikeObject(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + return baseTimes(length, function(index) { + return arrayMap(array, baseProperty(index)); + }); + } + + /** + * This method is like `_.unzip` except that it accepts `iteratee` to specify + * how regrouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee=_.identity] The function to combine + * regrouped values. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee) { + if (!(array && array.length)) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + return arrayMap(result, function(group) { + return apply(iteratee, undefined, group); + }); + } + + /** + * Creates an array excluding all given values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.pull`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.xor + * @example + * + * _.without([2, 1, 2, 3], 1, 2); + * // => [3] + */ + var without = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the + * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the given arrays. The order of result values is determined by the order + * they occur in the arrays. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.without + * @example + * + * _.xor([2, 1], [2, 3]); + * // => [1, 3] + */ + var xor = baseRest(function(arrays) { + return baseXor(arrayFilter(arrays, isArrayLikeObject)); + }); + + /** + * This method is like `_.xor` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which by which they're compared. The order of result values is determined + * by the order they occur in the arrays. The iteratee is invoked with one + * argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2, 3.4] + * + * // The `_.property` iteratee shorthand. + * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var xorBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.xor` except that it accepts `comparator` which is + * invoked to compare elements of `arrays`. The order of result values is + * determined by the order they occur in the arrays. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.xorWith(objects, others, _.isEqual); + * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var xorWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); + }); + + /** + * Creates an array of grouped elements, the first of which contains the + * first elements of the given arrays, the second of which contains the + * second elements of the given arrays, and so on. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + */ + var zip = baseRest(unzip); + + /** + * This method is like `_.fromPairs` except that it accepts two arrays, + * one of property identifiers and one of corresponding values. + * + * @static + * @memberOf _ + * @since 0.4.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject(['a', 'b'], [1, 2]); + * // => { 'a': 1, 'b': 2 } + */ + function zipObject(props, values) { + return baseZipObject(props || [], values || [], assignValue); + } + + /** + * This method is like `_.zipObject` except that it supports property paths. + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); + * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } + */ + function zipObjectDeep(props, values) { + return baseZipObject(props || [], values || [], baseSet); + } + + /** + * This method is like `_.zip` except that it accepts `iteratee` to specify + * how grouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee=_.identity] The function to combine + * grouped values. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { + * return a + b + c; + * }); + * // => [111, 222] + */ + var zipWith = baseRest(function(arrays) { + var length = arrays.length, + iteratee = length > 1 ? arrays[length - 1] : undefined; + + iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; + return unzipWith(arrays, iteratee); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` wrapper instance that wraps `value` with explicit method + * chain sequences enabled. The result of such sequences must be unwrapped + * with `_#value`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Seq + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _ + * .chain(users) + * .sortBy('age') + * .map(function(o) { + * return o.user + ' is ' + o.age; + * }) + * .head() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor + * is invoked with one argument; (value). The purpose of this method is to + * "tap into" a method chain sequence in order to modify intermediate results. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * // Mutate input array. + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * The purpose of this method is to "pass thru" values replacing intermediate + * results in a method chain sequence. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor) { + return interceptor(value); + } + + /** + * This method is the wrapper version of `_.at`. + * + * @name at + * @memberOf _ + * @since 1.0.0 + * @category Seq + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _(object).at(['a[0].b.c', 'a[1]']).value(); + * // => [3, 4] + */ + var wrapperAt = flatRest(function(paths) { + var length = paths.length, + start = length ? paths[0] : 0, + value = this.__wrapped__, + interceptor = function(object) { return baseAt(object, paths); }; + + if (length > 1 || this.__actions__.length || + !(value instanceof LazyWrapper) || !isIndex(start)) { + return this.thru(interceptor); + } + value = value.slice(start, +start + (length ? 1 : 0)); + value.__actions__.push({ + 'func': thru, + 'args': [interceptor], + 'thisArg': undefined + }); + return new LodashWrapper(value, this.__chain__).thru(function(array) { + if (length && !array.length) { + array.push(undefined); + } + return array; + }); + }); + + /** + * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. + * + * @name chain + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // A sequence without explicit chaining. + * _(users).head(); + * // => { 'user': 'barney', 'age': 36 } + * + * // A sequence with explicit chaining. + * _(users) + * .chain() + * .head() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chain sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Gets the next value on a wrapped object following the + * [iterator protocol](https://mdn.io/iteration_protocols#iterator). + * + * @name next + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the next iterator value. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped.next(); + * // => { 'done': false, 'value': 1 } + * + * wrapped.next(); + * // => { 'done': false, 'value': 2 } + * + * wrapped.next(); + * // => { 'done': true, 'value': undefined } + */ + function wrapperNext() { + if (this.__values__ === undefined) { + this.__values__ = toArray(this.value()); + } + var done = this.__index__ >= this.__values__.length, + value = done ? undefined : this.__values__[this.__index__++]; + + return { 'done': done, 'value': value }; + } + + /** + * Enables the wrapper to be iterable. + * + * @name Symbol.iterator + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the wrapper object. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped[Symbol.iterator]() === wrapped; + * // => true + * + * Array.from(wrapped); + * // => [1, 2] + */ + function wrapperToIterator() { + return this; + } + + /** + * Creates a clone of the chain sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @param {*} value The value to plant. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2]).map(square); + * var other = wrapped.plant([3, 4]); + * + * other.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + clone.__index__ = 0; + clone.__values__ = undefined; + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * This method is the wrapper version of `_.reverse`. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ + 'func': thru, + 'args': [reverse], + 'thisArg': undefined + }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(reverse); + } + + /** + * Executes the chain sequence to resolve the unwrapped value. + * + * @name value + * @memberOf _ + * @since 0.1.0 + * @alias toJSON, valueOf + * @category Seq + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the number of times the key was returned by `iteratee`. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': 1, '6': 2 } + * + * // The `_.property` iteratee shorthand. + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + ++result[key]; + } else { + baseAssignValue(result, key, 1); + } + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * **Note:** This method returns `true` for + * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty collections. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.every(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, guard) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * **Note:** Unlike `_.remove`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.reject + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.filter(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, { 'age': 36, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.filter(users, 'active'); + * // => objects for ['barney'] + */ + function filter(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.find(users, function(o) { return o.age < 40; }); + * // => object for 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.find(users, { 'age': 1, 'active': true }); + * // => object for 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.find(users, ['active', false]); + * // => object for 'fred' + * + * // The `_.property` iteratee shorthand. + * _.find(users, 'active'); + * // => object for 'barney' + */ + var find = createFind(findIndex); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=collection.length-1] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(findLastIndex); + + /** + * Creates a flattened array of values by running each element in `collection` + * thru `iteratee` and flattening the mapped results. The iteratee is invoked + * with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [n, n]; + * } + * + * _.flatMap([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMap(collection, iteratee) { + return baseFlatten(map(collection, iteratee), 1); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDeep([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMapDeep(collection, iteratee) { + return baseFlatten(map(collection, iteratee), INFINITY); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDepth([1, 2], duplicate, 2); + * // => [[1, 1], [2, 2]] + */ + function flatMapDepth(collection, iteratee, depth) { + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(map(collection, iteratee), depth); + } + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `_.forIn` + * or `_.forOwn` for object iteration. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEachRight + * @example + * + * _.forEach([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `1` then `2`. + * + * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forEach(collection, iteratee) { + var func = isArray(collection) ? arrayEach : baseEach; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @alias eachRight + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEach + * @example + * + * _.forEachRight([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `2` then `1`. + */ + function forEachRight(collection, iteratee) { + var func = isArray(collection) ? arrayEachRight : baseEachRight; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The order of grouped values + * is determined by the order they occur in `collection`. The corresponding + * value of each key is an array of elements responsible for generating the + * key. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': [4.2], '6': [6.1, 6.3] } + * + * // The `_.property` iteratee shorthand. + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + baseAssignValue(result, key, [value]); + } + }); + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * is used for equality comparisons. If `fromIndex` is negative, it's used as + * the offset from the end of `collection`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; + + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) + ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) + : (!!length && baseIndexOf(collection, value, fromIndex) > -1); + } + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `path` is a function, it's invoked + * for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke each method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invokeMap([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invokeMap = baseRest(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); + }); + return result; + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the last element responsible for generating the key. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var array = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.keyBy(array, function(o) { + * return String.fromCharCode(o.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.keyBy(array, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + */ + var keyBy = createAggregator(function(result, value, key) { + baseAssignValue(result, key, value); + }); + + /** + * Creates an array of values by running each element in `collection` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, + * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, + * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, + * `template`, `trim`, `trimEnd`, `trimStart`, and `words` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n; + * } + * + * _.map([4, 8], square); + * // => [16, 64] + * + * _.map({ 'a': 4, 'b': 8 }, square); + * // => [16, 64] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // The `_.property` iteratee shorthand. + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee) { + var func = isArray(collection) ? arrayMap : baseMap; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.sortBy` except that it allows specifying the sort + * orders of the iteratees to sort by. If `orders` is unspecified, all values + * are sorted in ascending order. Otherwise, specify an order of "desc" for + * descending or "asc" for ascending sort order of corresponding values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] + * The iteratees to sort by. + * @param {string[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // Sort by `user` in ascending order and by `age` in descending order. + * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + */ + function orderBy(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + orders = guard ? undefined : orders; + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, the second of which + * contains elements `predicate` returns falsey for. The predicate is + * invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * _.partition(users, function(o) { return o.active; }); + * // => objects for [['fred'], ['barney', 'pebbles']] + * + * // The `_.matches` iteratee shorthand. + * _.partition(users, { 'age': 1, 'active': false }); + * // => objects for [['pebbles'], ['barney', 'fred']] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.partition(users, ['active', false]); + * // => objects for [['barney', 'pebbles'], ['fred']] + * + * // The `_.property` iteratee shorthand. + * _.partition(users, 'active'); + * // => objects for [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `collection` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, + * and `sortBy` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduceRight + * @example + * + * _.reduce([1, 2], function(sum, n) { + * return sum + n; + * }, 0); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * return result; + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) + */ + function reduce(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduce : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); + } + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduce + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduceRight : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); + } + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.filter + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * _.reject(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.reject(users, { 'age': 40, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.reject(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.reject(users, 'active'); + * // => objects for ['barney'] + */ + function reject(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, negate(getIteratee(predicate, 3))); + } + + /** + * Gets a random element from `collection`. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + */ + function sample(collection) { + var func = isArray(collection) ? arraySample : baseSample; + return func(collection); + } + + /** + * Gets `n` random elements at unique keys from `collection` up to the + * size of `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @param {number} [n=1] The number of elements to sample. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the random elements. + * @example + * + * _.sampleSize([1, 2, 3], 2); + * // => [3, 1] + * + * _.sampleSize([1, 2, 3], 4); + * // => [2, 3, 1] + */ + function sampleSize(collection, n, guard) { + if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + var func = isArray(collection) ? arraySampleSize : baseSampleSize; + return func(collection, n); + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + var func = isArray(collection) ? arrayShuffle : baseShuffle; + return func(collection); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable string keyed properties for objects. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + if (collection == null) { + return 0; + } + if (isArrayLike(collection)) { + return isString(collection) ? stringSize(collection) : collection.length; + } + var tag = getTag(collection); + if (tag == mapTag || tag == setTag) { + return collection.size; + } + return baseKeys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.some(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, guard) { + var func = isArray(collection) ? arraySome : baseSome; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection thru each iteratee. This method + * performs a stable sort, that is, it preserves the original sort order of + * equal elements. The iteratees are invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to sort by. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.sortBy(users, [function(o) { return o.user; }]); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + * + * _.sortBy(users, ['user', 'age']); + * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] + */ + var sortBy = baseRest(function(collection, iteratees) { + if (collection == null) { + return []; + } + var length = iteratees.length; + if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { + iteratees = []; + } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { + iteratees = [iteratees[0]]; + } + return baseOrderBy(collection, baseFlatten(iteratees, 1), []); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now = ctxNow || function() { + return root.Date.now(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => Logs 'done saving!' after the two async saves have completed. + */ + function after(n, func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that invokes `func`, with up to `n` arguments, + * ignoring any additional arguments. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + n = guard ? undefined : n; + n = (func && n == null) ? func.length : n; + return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and `partials` prepended to the arguments it receives. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * function greet(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // Bound with placeholders. + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = baseRest(function(func, thisArg, partials) { + var bitmask = WRAP_BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bind)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(func, bitmask, thisArg, partials, holders); + }); + + /** + * Creates a function that invokes the method at `object[key]` with `partials` + * prepended to the arguments it receives. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. See + * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Function + * @param {Object} object The object to invoke the method on. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // Bound with placeholders. + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = baseRest(function(object, key, partials) { + var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bindKey)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts arguments of `func` and either invokes + * `func` returning its result, if at least `arity` number of arguments have + * been provided, or returns a function that accepts the remaining `func` + * arguments, and so on. The arity of `func` may be specified if `func.length` + * is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + function curry(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curry.placeholder; + return result; + } + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + function curryRight(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryRight.placeholder; + return result; + } + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + timeWaiting = wait - timeSinceLastCall; + + return maxing + ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) + : timeWaiting; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // => Logs 'deferred' after one millisecond. + */ + var defer = baseRest(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => Logs 'later' after one second. + */ + var delay = baseRest(function(func, wait, args) { + return baseDelay(func, toNumber(wait) || 0, args); + }); + + /** + * Creates a function that invokes `func` with arguments reversed. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to flip arguments for. + * @returns {Function} Returns the new flipped function. + * @example + * + * var flipped = _.flip(function() { + * return _.toArray(arguments); + * }); + * + * flipped('a', 'b', 'c', 'd'); + * // => ['d', 'c', 'b', 'a'] + */ + function flip(func) { + return createWrap(func, WRAP_FLIP_FLAG); + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Expose `MapCache`. + memoize.Cache = MapCache; + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new negated function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + var args = arguments; + switch (args.length) { + case 0: return !predicate.call(this); + case 1: return !predicate.call(this, args[0]); + case 2: return !predicate.call(this, args[0], args[1]); + case 3: return !predicate.call(this, args[0], args[1], args[2]); + } + return !predicate.apply(this, args); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with its arguments transformed. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms=[_.identity]] + * The argument transforms. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var func = _.overArgs(function(x, y) { + * return [x, y]; + * }, [square, doubled]); + * + * func(9, 3); + * // => [81, 6] + * + * func(10, 5); + * // => [100, 10] + */ + var overArgs = castRest(function(func, transforms) { + transforms = (transforms.length == 1 && isArray(transforms[0])) + ? arrayMap(transforms[0], baseUnary(getIteratee())) + : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); + + var funcsLength = transforms.length; + return baseRest(function(args) { + var index = -1, + length = nativeMin(args.length, funcsLength); + + while (++index < length) { + args[index] = transforms[index].call(this, args[index]); + } + return apply(func, this, args); + }); + }); + + /** + * Creates a function that invokes `func` with `partials` prepended to the + * arguments it receives. This method is like `_.bind` except it does **not** + * alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 0.2.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // Partially applied with placeholders. + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partial)); + return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); + }); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to the arguments it receives. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // Partially applied with placeholders. + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partialRight)); + return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); + }); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified `indexes` where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, [2, 0, 1]); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + */ + var rearg = flatRest(function(func, indexes) { + return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as + * an array. + * + * **Note:** This method is based on the + * [rest parameter](https://mdn.io/rest_parameters). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.rest(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function rest(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start === undefined ? start : toInteger(start); + return baseRest(func, start); + } + + /** + * Creates a function that invokes `func` with the `this` binding of the + * create function and an array of arguments much like + * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). + * + * **Note:** This method is based on the + * [spread operator](https://mdn.io/spread_operator). + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Function + * @param {Function} func The function to spread arguments over. + * @param {number} [start=0] The start position of the spread. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start == null ? 0 : nativeMax(toInteger(start), 0); + return baseRest(function(args) { + var array = args[start], + otherArgs = castSlice(args, 0, start); + + if (array) { + arrayPush(otherArgs, array); + } + return apply(func, this, otherArgs); + }); + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + /** + * Creates a function that accepts up to one argument, ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.unary(parseInt)); + * // => [6, 8, 10] + */ + function unary(func) { + return ary(func, 1); + } + + /** + * Creates a function that provides `value` to `wrapper` as its first + * argument. Any additional arguments provided to the function are appended + * to those provided to the `wrapper`. The wrapper is invoked with the `this` + * binding of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {*} value The value to wrap. + * @param {Function} [wrapper=identity] The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

' + func(text) + '

'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

fred, barney, & pebbles

' + */ + function wrap(value, wrapper) { + return partial(castFunction(wrapper), value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Casts `value` as an array if it's not one. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * _.castArray(1); + * // => [1] + * + * _.castArray({ 'a': 1 }); + * // => [{ 'a': 1 }] + * + * _.castArray('abc'); + * // => ['abc'] + * + * _.castArray(null); + * // => [null] + * + * _.castArray(undefined); + * // => [undefined] + * + * _.castArray(); + * // => [] + * + * var array = [1, 2, 3]; + * console.log(_.castArray(array) === array); + * // => true + */ + function castArray() { + if (!arguments.length) { + return []; + } + var value = arguments[0]; + return isArray(value) ? value : [value]; + } + + /** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. An empty object is returned for uncloneable values such + * as error objects, functions, DOM nodes, and WeakMaps. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see _.cloneDeep + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var shallow = _.clone(objects); + * console.log(shallow[0] === objects[0]); + * // => true + */ + function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * up to four arguments; (value [, index|key, object, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see _.cloneDeepWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * } + * + * var el = _.cloneWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 0 + */ + function cloneWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * This method is like `_.clone` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see _.clone + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var deep = _.cloneDeep(objects); + * console.log(deep[0] === objects[0]); + * // => false + */ + function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.cloneWith` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see _.cloneWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * } + * + * var el = _.cloneDeepWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 20 + */ + function cloneDeepWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `_.conforms` when `source` is + * partially applied. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); + * // => true + * + * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); + * // => false + */ + function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see _.lt + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + var gt = createRelationalOperation(baseGt); + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see _.lte + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + var gte = createRelationalOperation(function(value, other) { + return value >= other; + }); + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && + !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * _.isArrayBuffer(new ArrayBuffer(2)); + * // => true + * + * _.isArrayBuffer(new Array(2)); + * // => false + */ + var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || + (isObjectLike(value) && baseGetTag(value) == boolTag); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; + + /** + * Checks if `value` is likely a DOM element. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */ + function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); + } + + /** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && + (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || + isBuffer(value) || isTypedArray(value) || isArguments(value))) { + return !value.length; + } + var tag = getTag(value); + if (tag == mapTag || tag == setTag) { + return !value.size; + } + if (isPrototype(value)) { + return !baseKeys(value).length; + } + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * This method is like `_.isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true; + * } + * } + * + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqualWith(array, other, customizer); + * // => true + */ + function isEqualWith(value, other, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + if (!isObjectLike(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == errorTag || tag == domExcTag || + (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on + * [`Number.isFinite`](https://mdn.io/Number/isFinite). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(3); + * // => true + * + * _.isFinite(Number.MIN_VALUE); + * // => true + * + * _.isFinite(Infinity); + * // => false + * + * _.isFinite('3'); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ + function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `_.matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.isMatch(object, { 'b': 2 }); + * // => true + * + * _.isMatch(object, { 'b': 1 }); + * // => false + */ + function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); + } + + /** + * This method is like `_.isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true; + * } + * } + * + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatchWith(object, source, customizer); + * // => true + */ + function isMatchWith(object, source, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is based on + * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as + * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for + * `undefined` and other non-number values. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some + // ActiveX objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a pristine native function. + * + * **Note:** This method can't reliably detect native functions in the presence + * of the core-js package because core-js circumvents this kind of detection. + * Despite multiple requests, the core-js maintainer has made it clear: any + * attempt to fix the detection will be obstructed. As a result, we're left + * with little choice but to throw an error. Unfortunately, this also affects + * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), + * which rely on core-js. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (isMaskable(value)) { + throw new Error(CORE_ERROR_TEXT); + } + return baseIsNative(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is `null` or `undefined`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * _.isNil(null); + * // => true + * + * _.isNil(void 0); + * // => true + * + * _.isNil(NaN); + * // => false + */ + function isNil(value) { + return value == null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || + (isObjectLike(value) && baseGetTag(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; + + /** + * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 + * double precision number which isn't the result of a rounded unsafe integer. + * + * **Note:** This method is based on + * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. + * @example + * + * _.isSafeInteger(3); + * // => true + * + * _.isSafeInteger(Number.MIN_VALUE); + * // => false + * + * _.isSafeInteger(Infinity); + * // => false + * + * _.isSafeInteger('3'); + * // => false + */ + function isSafeInteger(value) { + return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Checks if `value` is `undefined`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is classified as a `WeakMap` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * _.isWeakMap(new WeakMap); + * // => true + * + * _.isWeakMap(new Map); + * // => false + */ + function isWeakMap(value) { + return isObjectLike(value) && getTag(value) == weakMapTag; + } + + /** + * Checks if `value` is classified as a `WeakSet` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * _.isWeakSet(new WeakSet); + * // => true + * + * _.isWeakSet(new Set); + * // => false + */ + function isWeakSet(value) { + return isObjectLike(value) && baseGetTag(value) == weakSetTag; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see _.gt + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + var lt = createRelationalOperation(baseLt); + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see _.gte + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + var lte = createRelationalOperation(function(value, other) { + return value <= other; + }); + + /** + * Converts `value` to an array. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * _.toArray({ 'a': 1, 'b': 2 }); + * // => [1, 2] + * + * _.toArray('abc'); + * // => ['a', 'b', 'c'] + * + * _.toArray(1); + * // => [] + * + * _.toArray(null); + * // => [] + */ + function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + var tag = getTag(value), + func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); + + return func(value); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; + } + + /** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toLength(3.2); + * // => 3 + * + * _.toLength(Number.MIN_VALUE); + * // => 0 + * + * _.toLength(Infinity); + * // => 4294967295 + * + * _.toLength('3.2'); + * // => 3 + */ + function toLength(value) { + return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toSafeInteger(3.2); + * // => 3 + * + * _.toSafeInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toSafeInteger(Infinity); + * // => 9007199254740991 + * + * _.toSafeInteger('3.2'); + * // => 3 + */ + function toSafeInteger(value) { + return value + ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) + : (value === 0 ? value : 0); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function(object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * This method is like `_.assign` except that it iterates over own and + * inherited source properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assign + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assignIn({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } + */ + var assignIn = createAssigner(function(object, source) { + copyObject(source, keysIn(source), object); + }); + + /** + * This method is like `_.assignIn` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extendWith + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignInWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keysIn(source), object, customizer); + }); + + /** + * This method is like `_.assign` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignInWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keys(source), object, customizer); + }); + + /** + * Creates an array of values corresponding to `paths` of `object`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _.at(object, ['a[0].b.c', 'a[1]']); + * // => [3, 4] + */ + var at = flatRest(baseAt); + + /** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties) { + var result = baseCreate(prototype); + return properties == null ? result : baseAssign(result, properties); + } + + /** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaultsDeep + * @example + * + * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var defaults = baseRest(function(object, sources) { + object = Object(object); + + var index = -1; + var length = sources.length; + var guard = length > 2 ? sources[2] : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + length = 1; + } + + while (++index < length) { + var source = sources[index]; + var props = keysIn(source); + var propsIndex = -1; + var propsLength = props.length; + + while (++propsIndex < propsLength) { + var key = props[propsIndex]; + var value = object[key]; + + if (value === undefined || + (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { + object[key] = source[key]; + } + } + } + + return object; + }); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaults + * @example + * + * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); + * // => { 'a': { 'b': 2, 'c': 3 } } + */ + var defaultsDeep = baseRest(function(args) { + args.push(undefined, customDefaultsMerge); + return apply(mergeWith, undefined, args); + }); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(o) { return o.age < 40; }); + * // => 'barney' (iteration order is not guaranteed) + * + * // The `_.matches` iteratee shorthand. + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findKey(users, 'active'); + * // => 'barney' + */ + function findKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); + } + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(o) { return o.age < 40; }); + * // => returns 'pebbles' assuming `_.findKey` returns 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + function findLastKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); + } + + /** + * Iterates over own and inherited enumerable string keyed properties of an + * object and invokes `iteratee` for each property. The iteratee is invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forInRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). + */ + function forIn(object, iteratee) { + return object == null + ? object + : baseFor(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forIn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. + */ + function forInRight(object, iteratee) { + return object == null + ? object + : baseForRight(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwnRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forOwn(object, iteratee) { + return object && baseForOwn(object, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. + */ + function forOwnRight(object, iteratee) { + return object && baseForOwnRight(object, getIteratee(iteratee, 3)); + } + + /** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functionsIn + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functions(new Foo); + * // => ['a', 'b'] + */ + function functions(object) { + return object == null ? [] : baseFunctions(object, keys(object)); + } + + /** + * Creates an array of function property names from own and inherited + * enumerable properties of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functions + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functionsIn(new Foo); + * // => ['a', 'b', 'c'] + */ + function functionsIn(object) { + return object == null ? [] : baseFunctions(object, keysIn(object)); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = { 'a': { 'b': 2 } }; + * var other = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b'); + * // => true + * + * _.has(object, ['a', 'b']); + * // => true + * + * _.has(other, 'a'); + * // => false + */ + function has(object, path) { + return object != null && hasPath(object, path, baseHas); + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + */ + var invert = createInverter(function(result, value, key) { + if (value != null && + typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + + result[value] = key; + }, constant(identity)); + + /** + * This method is like `_.invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invertBy(object); + * // => { '1': ['a', 'c'], '2': ['b'] } + * + * _.invertBy(object, function(value) { + * return 'group' + value; + * }); + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ + var invertBy = createInverter(function(result, value, key) { + if (value != null && + typeof value.toString != 'function') { + value = nativeObjectToString.call(value); + } + + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }, getIteratee); + + /** + * Invokes the method at `path` of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; + * + * _.invoke(object, 'a[0].b.c.slice', 1, 3); + * // => [2, 3] + */ + var invoke = baseRest(baseInvoke); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapValues + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + function mapKeys(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, iteratee(value, key, object), value); + }); + return result; + } + + /** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapKeys + * @example + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * _.mapValues(users, function(o) { return o.age; }); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + * + * // The `_.property` iteratee shorthand. + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + function mapValues(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, key, iteratee(value, key, object)); + }); + return result; + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method is like `_.merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (_.isArray(objValue)) { + * return objValue.concat(srcValue); + * } + * } + * + * var object = { 'a': [1], 'b': [2] }; + * var other = { 'a': [3], 'b': [4] }; + * + * _.mergeWith(object, other, customizer); + * // => { 'a': [1, 3], 'b': [2, 4] } + */ + var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { + baseMerge(object, source, srcIndex, customizer); + }); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable property paths of `object` that are not omitted. + * + * **Note:** This method is considerably slower than `_.pick`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to omit. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omit(object, ['a', 'c']); + * // => { 'b': '2' } + */ + var omit = flatRest(function(object, paths) { + var result = {}; + if (object == null) { + return result; + } + var isDeep = false; + paths = arrayMap(paths, function(path) { + path = castPath(path, object); + isDeep || (isDeep = path.length > 1); + return path; + }); + copyObject(object, getAllKeysIn(object), result); + if (isDeep) { + result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); + } + var length = paths.length; + while (length--) { + baseUnset(result, paths[length]); + } + return result; + }); + + /** + * The opposite of `_.pickBy`; this method creates an object composed of + * the own and inherited enumerable string keyed properties of `object` that + * `predicate` doesn't return truthy for. The predicate is invoked with two + * arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omitBy(object, _.isNumber); + * // => { 'b': '2' } + */ + function omitBy(object, predicate) { + return pickBy(object, negate(getIteratee(predicate))); + } + + /** + * Creates an object composed of the picked `object` properties. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pick(object, ['a', 'c']); + * // => { 'a': 1, 'c': 3 } + */ + var pick = flatRest(function(object, paths) { + return object == null ? {} : basePick(object, paths); + }); + + /** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pickBy(object, _.isNumber); + * // => { 'a': 1, 'c': 3 } + */ + function pickBy(object, predicate) { + if (object == null) { + return {}; + } + var props = arrayMap(getAllKeysIn(object), function(prop) { + return [prop]; + }); + predicate = getIteratee(predicate); + return basePickBy(object, props, function(value, path) { + return predicate(value, path[0]); + }); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = castPath(path, object); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + /** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `_.setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, ['x', '0', 'y', 'z'], 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); + } + + /** + * This method is like `_.set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.setWith(object, '[0][1]', 'a', Object); + * // => { '0': { '1': 'a' } } + */ + function setWith(object, path, value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); + } + + /** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ + var toPairs = createToPairs(keys); + + /** + * Creates an array of own and inherited enumerable string keyed-value pairs + * for `object` which can be consumed by `_.fromPairs`. If `object` is a map + * or set, its entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entriesIn + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairsIn(new Foo); + * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) + */ + var toPairsIn = createToPairs(keysIn); + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }, []); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function transform(object, iteratee, accumulator) { + var isArr = isArray(object), + isArrLike = isArr || isBuffer(object) || isTypedArray(object); + + iteratee = getIteratee(iteratee, 4); + if (accumulator == null) { + var Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor : []; + } + else if (isObject(object)) { + accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; + } + else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, ['a', '0', 'b', 'c']); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + + /** + * This method is like `_.set` except that accepts `updater` to produce the + * value to set. Use `_.updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.update(object, 'a[0].b.c', function(n) { return n * n; }); + * console.log(object.a[0].b.c); + * // => 9 + * + * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); + * console.log(object.x[0].y.z); + * // => 0 + */ + function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, castFunction(updater)); + } + + /** + * This method is like `_.update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.updateWith(object, '[0][1]', _.constant('a'), Object); + * // => { '0': { '1': 'a' } } + */ + function updateWith(object, path, updater, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); + } + + /** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return object == null ? [] : baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable string keyed property + * values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return object == null ? [] : baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * _.clamp(-10, -5, 5); + * // => -5 + * + * _.clamp(10, -5, 5); + * // => 5 + */ + function clamp(number, lower, upper) { + if (upper === undefined) { + upper = lower; + lower = undefined; + } + if (upper !== undefined) { + upper = toNumber(upper); + upper = upper === upper ? upper : 0; + } + if (lower !== undefined) { + lower = toNumber(lower); + lower = lower === lower ? lower : 0; + } + return baseClamp(toNumber(number), lower, upper); + } + + /** + * Checks if `n` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @static + * @memberOf _ + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see _.range, _.rangeRight + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + * + * _.inRange(-3, -2, -6); + * // => true + */ + function inRange(number, start, end) { + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + number = toNumber(number); + return baseInRange(number, start, end); + } + + /** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(lower, upper, floating) { + if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { + upper = floating = undefined; + } + if (floating === undefined) { + if (typeof upper == 'boolean') { + floating = upper; + upper = undefined; + } + else if (typeof lower == 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } + else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + var temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + var rand = nativeRandom(); + return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); + } + return baseRandom(lower, upper); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar--'); + * // => 'fooBar' + * + * _.camelCase('__FOO_BAR__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? capitalize(word) : word); + }); + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('FRED'); + * // => 'Fred' + */ + function capitalize(string) { + return upperFirst(toString(string).toLowerCase()); + } + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = toString(string); + target = baseToString(target); + + var length = string.length; + position = position === undefined + ? length + : baseClamp(toInteger(position), 0, length); + + var end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) == target; + } + + /** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + string = toString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https://lodash\.com/\)' + */ + function escapeRegExp(string) { + string = toString(string); + return (string && reHasRegExpChar.test(string)) + ? string.replace(reRegExpChar, '\\$&') + : string; + } + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Converts `string`, as space separated words, to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.lowerCase('--Foo-Bar--'); + * // => 'foo bar' + * + * _.lowerCase('fooBar'); + * // => 'foo bar' + * + * _.lowerCase('__FOO_BAR__'); + * // => 'foo bar' + */ + var lowerCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + word.toLowerCase(); + }); + + /** + * Converts the first character of `string` to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.lowerFirst('Fred'); + * // => 'fred' + * + * _.lowerFirst('FRED'); + * // => 'fRED' + */ + var lowerFirst = createCaseFirst('toLowerCase'); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string; + } + var mid = (length - strLength) / 2; + return ( + createPadding(nativeFloor(mid), chars) + + string + + createPadding(nativeCeil(mid), chars) + ); + } + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padEnd('abc', 6); + * // => 'abc ' + * + * _.padEnd('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padEnd('abc', 3); + * // => 'abc' + */ + function padEnd(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (string + createPadding(length - strLength, chars)) + : string; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padStart('abc', 6); + * // => ' abc' + * + * _.padStart('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padStart('abc', 3); + * // => 'abc' + */ + function padStart(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (createPadding(length - strLength, chars) + string) + : string; + } + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + if (guard || radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n, guard) { + if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + return baseRepeat(toString(string), n); + } + + /** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @example + * + * _.replace('Hi Fred', 'Fred', 'Barney'); + * // => 'Hi Barney' + */ + function replace() { + var args = arguments, + string = toString(args[0]); + + return args.length < 3 ? string : string.replace(args[1], args[2]); + } + + /** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--FOO-BAR--'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * _.split('a-b-c', '-', 2); + * // => ['a', 'b'] + */ + function split(string, separator, limit) { + if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { + separator = limit = undefined; + } + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + string = toString(string); + if (string && ( + typeof separator == 'string' || + (separator != null && !isRegExp(separator)) + )) { + separator = baseToString(separator); + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); + } + + /** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar--'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__FOO_BAR__'); + * // => 'FOO BAR' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + upperFirst(word); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = toString(string); + position = position == null + ? 0 + : baseClamp(toInteger(position), 0, string.length); + + target = baseToString(target); + return string.slice(position, position + target.length) == target; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is given, it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options={}] The options object. + * @param {RegExp} [options.escape=_.templateSettings.escape] + * The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] + * The "evaluate" delimiter. + * @param {Object} [options.imports=_.templateSettings.imports] + * An object to import into the template as free variables. + * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] + * The "interpolate" delimiter. + * @param {string} [options.sourceURL='lodash.templateSources[n]'] + * The sourceURL of the compiled template. + * @param {string} [options.variable='obj'] + * The data object variable name. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the compiled template function. + * @example + * + * // Use the "interpolate" delimiter to create a compiled template. + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // Use the HTML "escape" delimiter to escape data property values. + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': ' + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 74c4fda1c2..3aec801c86 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -589,6 +589,78 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + // https://github.com/webcomponents/polyfills/tree/master/packages/shadydom + it('should record shadow doms polyfilled by shadydom', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + // insert shadydom script + replaceLast( + getHtml.call(this, 'polyfilled-shadowdom-mutation.html'), + '', + ` + + + + `, + ), + ); + await page.evaluate(() => { + const target3 = document.querySelector('#target3'); + target3?.attachShadow({ + mode: 'open', + }); + target3?.shadowRoot?.appendChild(document.createElement('span')); + }); + await waitForRAF(page); // wait till browser sent snapshots + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + // https://github.com/salesforce/lwc/tree/master/packages/%40lwc/synthetic-shadow + it('should record shadow doms polyfilled by synthetic-shadow', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + // insert lwc's synthetic-shadow script + replaceLast( + getHtml.call(this, 'polyfilled-shadowdom-mutation.html'), + '', + ` + + + + `, + ), + ); + await page.evaluate(() => { + const target3 = document.querySelector('#target3'); + // create a shadow dom with synthetic shadow + // https://github.com/salesforce/lwc/blob/v2.20.3/packages/@lwc/synthetic-shadow/src/faux-shadow/element.ts#L81-L87 + target3?.attachShadow({ + mode: 'open', + '$$lwc-synthetic-mode': true, + } as ShadowRootInit); + target3?.shadowRoot?.appendChild(document.createElement('span')); + const target4 = document.createElement('div'); + target4.id = 'target4'; + // create a native shadow dom + document.body.appendChild(target4); + target4.attachShadow({ + mode: 'open', + }); + target4.shadowRoot?.appendChild(document.createElement('ul')); + }); + await waitForRAF(page); // wait till browser sent snapshots + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('should mask texts', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From df9d5bb02c24ea7fa0e5b0d3c70cb2bf2786283f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sat, 23 Jul 2022 12:51:51 +0800 Subject: [PATCH 032/201] fix: eslint action error in a PR from a fork repo (#943) * fix: eslint action error in a PR from a fork repo error message: 'Resource not accessible by integration' * try to fix the github action error --- .github/workflows/eslint.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 916763871a..a9f7736953 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -24,13 +24,24 @@ jobs: run: yarn lint:report # Continue to the next step even if this fails continue-on-error: true + - name: Upload ESLint report + uses: actions/upload-artifact@v3 + with: + name: eslint_report.json + path: eslint_report.json + + Annotation: + # Skip the annotation action in PRs from the forked repositories + if: github.event.pull_request.head.repo.full_name == 'rrweb-io/rrweb' + needs: eslint_check_upload + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 + with: + name: eslint_report.json - name: Annotate Code Linting Results uses: ataylorme/eslint-annotate-action@v2 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' report-json: 'eslint_report.json' - - name: Upload ESLint report - uses: actions/upload-artifact@v2 - with: - name: eslint_report.json - path: eslint_report.json From ba4bcbea8dba888dd31df25c79c7803163c56e72 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Mon, 25 Jul 2022 16:44:55 +0100 Subject: [PATCH 033/201] Bump jsdom to latest version as was getting error TS2305: Module '"parse5"' has no exported member 'ElementLocation'. (#945) --- packages/rrweb-snapshot/package.json | 2 +- yarn.lock | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 3e20883af0..d736b8c126 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -42,7 +42,7 @@ "devDependencies": { "@types/chai": "^4.1.4", "@types/jest": "^27.0.2", - "@types/jsdom": "^16.2.4", + "@types/jsdom": "^20.0.0", "@types/node": "^10.11.3", "@types/puppeteer": "^1.12.4", "cross-env": "^5.2.0", diff --git a/yarn.lock b/yarn.lock index 3699f0d003..f74e546741 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2054,14 +2054,14 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/jsdom@^16.2.4": - version "16.2.13" - resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.13.tgz" - integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw== +"@types/jsdom@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" + integrity sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA== dependencies: "@types/node" "*" - "@types/parse5" "*" "@types/tough-cookie" "*" + parse5 "^7.0.0" "@types/json-schema@^7.0.9": version "7.0.11" @@ -2113,11 +2113,6 @@ resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/parse5@*": - version "6.0.1" - resolved "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.1.tgz" - integrity sha512-ARATsLdrGPUnaBvxLhUlnltcMgn7pQG312S8ccdYlnyijabrX9RN/KN/iGj9Am96CoW8e/K9628BA7Bv4XHdrA== - "@types/pixelmatch@*": version "5.2.4" resolved "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.4.tgz" @@ -3915,7 +3910,6 @@ csso@^4.0.2: cssom@^0.4.4, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" - uid ed298055b97cbddcdeb278f904857629dec5e0e1 resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" cssom@^0.5.0: @@ -4287,6 +4281,11 @@ entities@^2.0.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" + integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg== + entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -8672,6 +8671,13 @@ parse5@6.0.1, parse5@^6.0.1: resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a" + integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g== + dependencies: + entities "^4.3.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" From 16089fc6b52a123092b736730553916b4e71eed4 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Mon, 25 Jul 2022 23:56:01 +0800 Subject: [PATCH 034/201] fix console plugin example --- docs/recipes/console.md | 34 ++++++++++++++++++++-------------- docs/recipes/console.zh_CN.md | 34 ++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/docs/recipes/console.md b/docs/recipes/console.md index c035271d2a..ffeb55f18d 100644 --- a/docs/recipes/console.md +++ b/docs/recipes/console.md @@ -9,9 +9,11 @@ You can enable the logger using default option like this: ```js rrweb.record({ - emit: emit(event) { + emit: function emit(event) { // you should use console.log in this way to avoid errors. - const defaultLog = console.log["__rrweb_original__"] ? console.log["__rrweb_original__"] : console.log; + const defaultLog = console.log['__rrweb_original__'] + ? console.log['__rrweb_original__'] + : console.log; defaultLog(event); }, // to use default record option @@ -26,22 +28,26 @@ You can also customize the behavior of logger like this: ```js rrweb.record({ - emit: emit(event) { + emit: function emit(event) { // you should use console.log in this way to avoid errors. - const defaultLog = console.log["__rrweb_original__"] ? console.log["__rrweb_original__"] : console.log; + const defaultLog = console.log['__rrweb_original__'] + ? console.log['__rrweb_original__'] + : console.log; defaultLog(event); }, // customized options - plugins: [rrweb.getRecordConsolePlugin({ - level: ["info", "log", "warn", "error"], - lengthThreshold: 10000, - stringifyOptions: { - stringLengthLimit: 1000, - numOfKeysLimit: 100, - depthOfLimit: 1 - }, - logger: window.console, - })], + plugins: [ + rrweb.getRecordConsolePlugin({ + level: ['info', 'log', 'warn', 'error'], + lengthThreshold: 10000, + stringifyOptions: { + stringLengthLimit: 1000, + numOfKeysLimit: 100, + depthOfLimit: 1, + }, + logger: window.console, + }), + ], }); ``` diff --git a/docs/recipes/console.zh_CN.md b/docs/recipes/console.zh_CN.md index 5b63611a76..3171548427 100644 --- a/docs/recipes/console.zh_CN.md +++ b/docs/recipes/console.zh_CN.md @@ -8,9 +8,11 @@ ```js rrweb.record({ - emit: emit(event) { + emit: function emit(event) { // 如果要使用console来输出信息,请使用如下的写法 - const defaultLog = console.log["__rrweb_original__"] ? console.log["__rrweb_original__"] : console.log; + const defaultLog = console.log['__rrweb_original__'] + ? console.log['__rrweb_original__'] + : console.log; defaultLog(event); }, // 使用默认的配置选项 @@ -25,22 +27,26 @@ rrweb.record({ ```js rrweb.record({ - emit: emit(event) { + emit: function emit(event) { // 如果要使用console来输出信息,请使用如下的写法 - const defaultLog = console.log["__rrweb_original__"] ? console.log["__rrweb_original__"] : console.log; + const defaultLog = console.log['__rrweb_original__'] + ? console.log['__rrweb_original__'] + : console.log; defaultLog(event); }, // 定制的选项 - plugins: [rrweb.getRecordConsolePlugin({ - level: ["info", "log", "warn", "error"], - lengthThreshold: 10000, - stringifyOptions: { - stringLengthLimit: 1000, - numOfKeysLimit: 100, - depthOfLimit: 1 - }, - logger: window.console, - })], + plugins: [ + rrweb.getRecordConsolePlugin({ + level: ['info', 'log', 'warn', 'error'], + lengthThreshold: 10000, + stringifyOptions: { + stringLengthLimit: 1000, + numOfKeysLimit: 100, + depthOfLimit: 1, + }, + logger: window.console, + }), + ], }); ``` From b618f095df1d7c8e17c7b270d401e078c8ac8f5c Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Tue, 26 Jul 2022 15:54:27 +0100 Subject: [PATCH 035/201] Remove typings files as these can be regenerated with `npm run typings` from their `src/types.ts` masters, and are an extra source of conflicts if they remain in git (#946) --- packages/rrweb-snapshot/typings/types.d.ts | 115 ----- packages/rrweb/typings/types.d.ts | 525 --------------------- 2 files changed, 640 deletions(-) delete mode 100644 packages/rrweb-snapshot/typings/types.d.ts delete mode 100644 packages/rrweb/typings/types.d.ts diff --git a/packages/rrweb-snapshot/typings/types.d.ts b/packages/rrweb-snapshot/typings/types.d.ts deleted file mode 100644 index 5282993e41..0000000000 --- a/packages/rrweb-snapshot/typings/types.d.ts +++ /dev/null @@ -1,115 +0,0 @@ -export declare enum NodeType { - Document = 0, - DocumentType = 1, - Element = 2, - Text = 3, - CDATA = 4, - Comment = 5 -} -export declare type documentNode = { - type: NodeType.Document; - childNodes: serializedNodeWithId[]; - compatMode?: string; -}; -export declare type documentTypeNode = { - type: NodeType.DocumentType; - name: string; - publicId: string; - systemId: string; -}; -export declare type attributes = { - [key: string]: string | number | boolean; -}; -export declare type elementNode = { - type: NodeType.Element; - tagName: string; - attributes: attributes; - childNodes: serializedNodeWithId[]; - isSVG?: true; - needBlock?: boolean; -}; -export declare type textNode = { - type: NodeType.Text; - textContent: string; - isStyle?: true; -}; -export declare type cdataNode = { - type: NodeType.CDATA; - textContent: ''; -}; -export declare type commentNode = { - type: NodeType.Comment; - textContent: string; -}; -export declare type serializedNode = (documentNode | documentTypeNode | elementNode | textNode | cdataNode | commentNode) & { - rootId?: number; - isShadowHost?: boolean; - isShadow?: boolean; -}; -export declare type serializedNodeWithId = serializedNode & { - id: number; -}; -export declare type serializedElementNodeWithId = Extract>; -export declare type tagMap = { - [key: string]: string; -}; -export interface INode extends Node { - __sn: serializedNodeWithId; -} -export interface ICanvas extends HTMLCanvasElement { - __context: string; -} -export interface IMirror { - getId(n: TNode | undefined | null): number; - getNode(id: number): TNode | null; - getIds(): number[]; - getMeta(n: TNode): serializedNodeWithId | null; - removeNodeFromMap(n: TNode): void; - has(id: number): boolean; - hasNode(node: TNode): boolean; - add(n: TNode, meta: serializedNodeWithId): void; - replace(id: number, n: TNode): void; - reset(): void; -} -export declare type idNodeMap = Map; -export declare type nodeMetaMap = WeakMap; -export declare type MaskInputOptions = Partial<{ - color: boolean; - date: boolean; - 'datetime-local': boolean; - email: boolean; - month: boolean; - number: boolean; - range: boolean; - search: boolean; - tel: boolean; - text: boolean; - time: boolean; - url: boolean; - week: boolean; - textarea: boolean; - select: boolean; - password: boolean; -}>; -export declare type SlimDOMOptions = Partial<{ - script: boolean; - comment: boolean; - headFavicon: boolean; - headWhitespace: boolean; - headMetaDescKeywords: boolean; - headMetaSocial: boolean; - headMetaRobots: boolean; - headMetaHttpEquiv: boolean; - headMetaAuthorship: boolean; - headMetaVerification: boolean; -}>; -export declare type DataURLOptions = Partial<{ - type: string; - quality: number; -}>; -export declare type MaskTextFn = (text: string) => string; -export declare type MaskInputFn = (text: string) => string; -export declare type KeepIframeSrcFn = (src: string) => boolean; -export declare type BuildCache = { - stylesWithHoverClass: Map; -}; diff --git a/packages/rrweb/typings/types.d.ts b/packages/rrweb/typings/types.d.ts deleted file mode 100644 index 8ab2913771..0000000000 --- a/packages/rrweb/typings/types.d.ts +++ /dev/null @@ -1,525 +0,0 @@ -import type { serializedNodeWithId, Mirror, INode, MaskInputOptions, SlimDOMOptions, MaskInputFn, MaskTextFn } from 'rrweb-snapshot'; -import type { PackFn, UnpackFn } from './packer/base'; -import type { IframeManager } from './record/iframe-manager'; -import type { ShadowDomManager } from './record/shadow-dom-manager'; -import type { Replayer } from './replay'; -import type { RRNode } from 'rrdom'; -import type { CanvasManager } from './record/observers/canvas/canvas-manager'; -import type { StylesheetManager } from './record/stylesheet-manager'; -export declare enum EventType { - DomContentLoaded = 0, - Load = 1, - FullSnapshot = 2, - IncrementalSnapshot = 3, - Meta = 4, - Custom = 5, - Plugin = 6 -} -export declare type domContentLoadedEvent = { - type: EventType.DomContentLoaded; - data: unknown; -}; -export declare type loadedEvent = { - type: EventType.Load; - data: unknown; -}; -export declare type fullSnapshotEvent = { - type: EventType.FullSnapshot; - data: { - node: serializedNodeWithId; - initialOffset: { - top: number; - left: number; - }; - }; -}; -export declare type incrementalSnapshotEvent = { - type: EventType.IncrementalSnapshot; - data: incrementalData; -}; -export declare type metaEvent = { - type: EventType.Meta; - data: { - href: string; - width: number; - height: number; - }; -}; -export declare type customEvent = { - type: EventType.Custom; - data: { - tag: string; - payload: T; - }; -}; -export declare type pluginEvent = { - type: EventType.Plugin; - data: { - plugin: string; - payload: T; - }; -}; -export declare enum IncrementalSource { - Mutation = 0, - MouseMove = 1, - MouseInteraction = 2, - Scroll = 3, - ViewportResize = 4, - Input = 5, - TouchMove = 6, - MediaInteraction = 7, - StyleSheetRule = 8, - CanvasMutation = 9, - Font = 10, - Log = 11, - Drag = 12, - StyleDeclaration = 13 -} -export declare type mutationData = { - source: IncrementalSource.Mutation; -} & mutationCallbackParam; -export declare type mousemoveData = { - source: IncrementalSource.MouseMove | IncrementalSource.TouchMove | IncrementalSource.Drag; - positions: mousePosition[]; -}; -export declare type mouseInteractionData = { - source: IncrementalSource.MouseInteraction; -} & mouseInteractionParam; -export declare type scrollData = { - source: IncrementalSource.Scroll; -} & scrollPosition; -export declare type viewportResizeData = { - source: IncrementalSource.ViewportResize; -} & viewportResizeDimension; -export declare type inputData = { - source: IncrementalSource.Input; - id: number; -} & inputValue; -export declare type mediaInteractionData = { - source: IncrementalSource.MediaInteraction; -} & mediaInteractionParam; -export declare type styleSheetRuleData = { - source: IncrementalSource.StyleSheetRule; -} & styleSheetRuleParam; -export declare type styleDeclarationData = { - source: IncrementalSource.StyleDeclaration; -} & styleDeclarationParam; -export declare type canvasMutationData = { - source: IncrementalSource.CanvasMutation; -} & canvasMutationParam; -export declare type fontData = { - source: IncrementalSource.Font; -} & fontParam; -export declare type incrementalData = mutationData | mousemoveData | mouseInteractionData | scrollData | viewportResizeData | inputData | mediaInteractionData | styleSheetRuleData | canvasMutationData | fontData | styleDeclarationData; -export declare type event = domContentLoadedEvent | loadedEvent | fullSnapshotEvent | incrementalSnapshotEvent | metaEvent | customEvent | pluginEvent; -export declare type eventWithTime = event & { - timestamp: number; - delay?: number; -}; -export declare type canvasEventWithTime = eventWithTime & { - type: EventType.IncrementalSnapshot; - data: canvasMutationData; -}; -export declare type blockClass = string | RegExp; -export declare type maskTextClass = string | RegExp; -export declare type SamplingStrategy = Partial<{ - mousemove: boolean | number; - mousemoveCallback: number; - mouseInteraction: boolean | Record; - scroll: number; - media: number; - input: 'all' | 'last'; - canvas: 'all' | number; -}>; -export declare type RecordPlugin = { - name: string; - observer?: (cb: (...args: Array) => void, win: IWindow, options: TOptions) => listenerHandler; - eventProcessor?: (event: eventWithTime) => eventWithTime & TExtend; - options: TOptions; -}; -export declare type recordOptions = { - emit?: (e: T, isCheckout?: boolean) => void; - checkoutEveryNth?: number; - checkoutEveryNms?: number; - blockClass?: blockClass; - blockSelector?: string; - ignoreClass?: string; - maskTextClass?: maskTextClass; - maskTextSelector?: string; - maskAllInputs?: boolean; - maskInputOptions?: MaskInputOptions; - maskInputFn?: MaskInputFn; - maskTextFn?: MaskTextFn; - slimDOMOptions?: SlimDOMOptions | 'all' | true; - inlineStylesheet?: boolean; - hooks?: hooksParam; - packFn?: PackFn; - sampling?: SamplingStrategy; - recordCanvas?: boolean; - userTriggeredOnInput?: boolean; - collectFonts?: boolean; - inlineImages?: boolean; - plugins?: RecordPlugin[]; - mousemoveWait?: number; - keepIframeSrcFn?: KeepIframeSrcFn; -}; -export declare type observerParam = { - mutationCb: mutationCallBack; - mousemoveCb: mousemoveCallBack; - mouseInteractionCb: mouseInteractionCallBack; - scrollCb: scrollCallback; - viewportResizeCb: viewportResizeCallback; - inputCb: inputCallback; - mediaInteractionCb: mediaInteractionCallback; - blockClass: blockClass; - blockSelector: string | null; - ignoreClass: string; - maskTextClass: maskTextClass; - maskTextSelector: string | null; - maskInputOptions: MaskInputOptions; - maskInputFn?: MaskInputFn; - maskTextFn?: MaskTextFn; - inlineStylesheet: boolean; - styleSheetRuleCb: styleSheetRuleCallback; - styleDeclarationCb: styleDeclarationCallback; - canvasMutationCb: canvasMutationCallback; - fontCb: fontCallback; - sampling: SamplingStrategy; - recordCanvas: boolean; - inlineImages: boolean; - userTriggeredOnInput: boolean; - collectFonts: boolean; - slimDOMOptions: SlimDOMOptions; - doc: Document; - mirror: Mirror; - iframeManager: IframeManager; - stylesheetManager: StylesheetManager; - shadowDomManager: ShadowDomManager; - canvasManager: CanvasManager; - plugins: Array<{ - observer: (cb: (...arg: Array) => void, win: IWindow, options: unknown) => listenerHandler; - callback: (...arg: Array) => void; - options: unknown; - }>; -}; -export declare type MutationBufferParam = Pick; -export declare type hooksParam = { - mutation?: mutationCallBack; - mousemove?: mousemoveCallBack; - mouseInteraction?: mouseInteractionCallBack; - scroll?: scrollCallback; - viewportResize?: viewportResizeCallback; - input?: inputCallback; - mediaInteaction?: mediaInteractionCallback; - styleSheetRule?: styleSheetRuleCallback; - styleDeclaration?: styleDeclarationCallback; - canvasMutation?: canvasMutationCallback; - font?: fontCallback; -}; -export declare type mutationRecord = { - type: string; - target: Node; - oldValue: string | null; - addedNodes: NodeList; - removedNodes: NodeList; - attributeName: string | null; -}; -export declare type textCursor = { - node: Node; - value: string | null; -}; -export declare type textMutation = { - id: number; - value: string | null; -}; -export declare type styleAttributeValue = { - [key: string]: styleValueWithPriority | string | false; -}; -export declare type styleValueWithPriority = [string, string]; -export declare type attributeCursor = { - node: Node; - attributes: { - [key: string]: string | styleAttributeValue | null; - }; -}; -export declare type attributeMutation = { - id: number; - attributes: { - [key: string]: string | styleAttributeValue | null; - }; -}; -export declare type removedNodeMutation = { - parentId: number; - id: number; - isShadow?: boolean; -}; -export declare type addedNodeMutation = { - parentId: number; - previousId?: number | null; - nextId: number | null; - node: serializedNodeWithId; -}; -export declare type mutationCallbackParam = { - texts: textMutation[]; - attributes: attributeMutation[]; - removes: removedNodeMutation[]; - adds: addedNodeMutation[]; - isAttachIframe?: true; -}; -export declare type mutationCallBack = (m: mutationCallbackParam) => void; -export declare type mousemoveCallBack = (p: mousePosition[], source: IncrementalSource.MouseMove | IncrementalSource.TouchMove | IncrementalSource.Drag) => void; -export declare type mousePosition = { - x: number; - y: number; - id: number; - timeOffset: number; -}; -export declare type mouseMovePos = { - x: number; - y: number; - id: number; - debugData: incrementalData; -}; -export declare enum MouseInteractions { - MouseUp = 0, - MouseDown = 1, - Click = 2, - ContextMenu = 3, - DblClick = 4, - Focus = 5, - Blur = 6, - TouchStart = 7, - TouchMove_Departed = 8, - TouchEnd = 9, - TouchCancel = 10 -} -export declare enum CanvasContext { - '2D' = 0, - WebGL = 1, - WebGL2 = 2 -} -export declare type SerializedCanvasArg = { - rr_type: 'ArrayBuffer'; - base64: string; -} | { - rr_type: 'Blob'; - data: Array; - type?: string; -} | { - rr_type: string; - src: string; -} | { - rr_type: string; - args: Array; -} | { - rr_type: string; - index: number; -}; -export declare type CanvasArg = SerializedCanvasArg | string | number | boolean | null | CanvasArg[]; -declare type mouseInteractionParam = { - type: MouseInteractions; - id: number; - x: number; - y: number; -}; -export declare type mouseInteractionCallBack = (d: mouseInteractionParam) => void; -export declare type scrollPosition = { - id: number; - x: number; - y: number; -}; -export declare type scrollCallback = (p: scrollPosition) => void; -export declare type styleSheetAddRule = { - rule: string; - index?: number | number[]; -}; -export declare type styleSheetDeleteRule = { - index: number | number[]; -}; -export declare type styleSheetRuleParam = { - id: number; - removes?: styleSheetDeleteRule[]; - adds?: styleSheetAddRule[]; -}; -export declare type styleSheetRuleCallback = (s: styleSheetRuleParam) => void; -export declare type styleDeclarationParam = { - id: number; - index: number[]; - set?: { - property: string; - value: string | null; - priority: string | undefined; - }; - remove?: { - property: string; - }; -}; -export declare type styleDeclarationCallback = (s: styleDeclarationParam) => void; -export declare type canvasMutationCommand = { - property: string; - args: Array; - setter?: true; -}; -export declare type canvasMutationParam = { - id: number; - type: CanvasContext; - commands: canvasMutationCommand[]; -} | ({ - id: number; - type: CanvasContext; -} & canvasMutationCommand); -export declare type canvasMutationWithType = { - type: CanvasContext; -} & canvasMutationCommand; -export declare type canvasMutationCallback = (p: canvasMutationParam) => void; -export declare type canvasManagerMutationCallback = (target: HTMLCanvasElement, p: canvasMutationWithType) => void; -export declare type ImageBitmapDataURLWorkerParams = { - id: number; - bitmap: ImageBitmap; - width: number; - height: number; -}; -export declare type ImageBitmapDataURLWorkerResponse = { - id: number; -} | { - id: number; - type: string; - base64: string; - width: number; - height: number; -}; -export declare type fontParam = { - family: string; - fontSource: string; - buffer: boolean; - descriptors?: FontFaceDescriptors; -}; -export declare type fontCallback = (p: fontParam) => void; -export declare type viewportResizeDimension = { - width: number; - height: number; -}; -export declare type viewportResizeCallback = (d: viewportResizeDimension) => void; -export declare type inputValue = { - text: string; - isChecked: boolean; - userTriggered?: boolean; -}; -export declare type inputCallback = (v: inputValue & { - id: number; -}) => void; -export declare const enum MediaInteractions { - Play = 0, - Pause = 1, - Seeked = 2, - VolumeChange = 3 -} -export declare type mediaInteractionParam = { - type: MediaInteractions; - id: number; - currentTime?: number; - volume?: number; - muted?: boolean; -}; -export declare type mediaInteractionCallback = (p: mediaInteractionParam) => void; -export declare type DocumentDimension = { - x: number; - y: number; - relativeScale: number; - absoluteScale: number; -}; -export declare type DeprecatedMirror = { - map: { - [key: number]: INode; - }; - getId: (n: Node) => number; - getNode: (id: number) => INode | null; - removeNodeFromMap: (n: Node) => void; - has: (id: number) => boolean; - reset: () => void; -}; -export declare type throttleOptions = { - leading?: boolean; - trailing?: boolean; -}; -export declare type listenerHandler = () => void; -export declare type hookResetter = () => void; -export declare type ReplayPlugin = { - handler: (event: eventWithTime, isSync: boolean, context: { - replayer: Replayer; - }) => void; -}; -export declare type playerConfig = { - speed: number; - maxSpeed: number; - root: Element; - loadTimeout: number; - skipInactive: boolean; - showWarning: boolean; - showDebug: boolean; - blockClass: string; - liveMode: boolean; - insertStyleRules: string[]; - triggerFocus: boolean; - UNSAFE_replayCanvas: boolean; - pauseAnimation?: boolean; - mouseTail: boolean | { - duration?: number; - lineCap?: string; - lineWidth?: number; - strokeStyle?: string; - }; - unpackFn?: UnpackFn; - useVirtualDom: boolean; - plugins?: ReplayPlugin[]; -}; -export declare type playerMetaData = { - startTime: number; - endTime: number; - totalTime: number; -}; -export declare type missingNode = { - node: Node | RRNode; - mutation: addedNodeMutation; -}; -export declare type missingNodeMap = { - [id: number]: missingNode; -}; -export declare type actionWithDelay = { - doAction: () => void; - delay: number; -}; -export declare type Handler = (event?: unknown) => void; -export declare type Emitter = { - on(type: string, handler: Handler): void; - emit(type: string, event?: unknown): void; - off(type: string, handler: Handler): void; -}; -export declare type Arguments = T extends (...payload: infer U) => unknown ? U : unknown; -export declare enum ReplayerEvents { - Start = "start", - Pause = "pause", - Resume = "resume", - Resize = "resize", - Finish = "finish", - FullsnapshotRebuilded = "fullsnapshot-rebuilded", - LoadStylesheetStart = "load-stylesheet-start", - LoadStylesheetEnd = "load-stylesheet-end", - SkipStart = "skip-start", - SkipEnd = "skip-end", - MouseInteraction = "mouse-interaction", - EventCast = "event-cast", - CustomEvent = "custom-event", - Flush = "flush", - StateChange = "state-change", - PlayBack = "play-back" -} -export declare type KeepIframeSrcFn = (src: string) => boolean; -declare global { - interface Window { - FontFace: typeof FontFace; - } -} -export declare type IWindow = Window & typeof globalThis; -export declare type Optional = Pick, K> & Omit; -export {}; From aecaefbf45f0ec946d194404d994031d8c947f74 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 31 Jul 2022 08:38:22 +0800 Subject: [PATCH 036/201] chore: remove all typings, add them all to .gitignore file and update turbo.json (#949) --- packages/rrweb-snapshot/.gitignore | 1 + packages/rrweb-snapshot/typings/css.d.ts | 92 ------------------- packages/rrweb-snapshot/typings/index.d.ts | 5 - packages/rrweb-snapshot/typings/rebuild.d.ts | 21 ----- packages/rrweb-snapshot/typings/snapshot.d.ts | 59 ------------ packages/rrweb-snapshot/typings/utils.d.ts | 27 ------ packages/rrweb/.gitignore | 1 + packages/rrweb/typings/entries/all.d.ts | 4 - .../rrweb/typings/entries/record-pack.d.ts | 2 - .../rrweb/typings/entries/replay-unpack.d.ts | 2 - packages/rrweb/typings/index.d.ts | 8 -- packages/rrweb/typings/packer/base.d.ts | 7 -- packages/rrweb/typings/packer/index.d.ts | 2 - packages/rrweb/typings/packer/pack.d.ts | 2 - packages/rrweb/typings/packer/unpack.d.ts | 2 - .../console/record/error-stack-parser.d.ts | 37 -------- .../typings/plugins/console/record/index.d.ts | 42 --------- .../plugins/console/record/stringify.d.ts | 2 - .../typings/plugins/console/replay/index.d.ts | 9 -- .../plugins/sequential-id/record/index.d.ts | 6 -- .../plugins/sequential-id/replay/index.d.ts | 7 -- .../rrweb/typings/record/iframe-manager.d.ts | 13 --- packages/rrweb/typings/record/index.d.ts | 9 -- packages/rrweb/typings/record/mutation.d.ts | 42 --------- packages/rrweb/typings/record/observer.d.ts | 7 -- .../typings/record/observers/canvas/2d.d.ts | 3 - .../observers/canvas/canvas-manager.d.ts | 35 ------- .../record/observers/canvas/canvas.d.ts | 2 - .../observers/canvas/serialize-args.d.ts | 6 -- .../record/observers/canvas/webgl.d.ts | 3 - .../record/observers/canvas/webgl2.d.ts | 2 - .../typings/record/shadow-dom-manager.d.ts | 22 ----- .../typings/record/stylesheet-manager.d.ts | 12 --- .../workers/image-bitmap-data-url-worker.d.ts | 5 - packages/rrweb/typings/replay/canvas/2d.d.ts | 9 -- .../replay/canvas/deserialize-args.d.ts | 7 -- .../rrweb/typings/replay/canvas/index.d.ts | 10 -- .../rrweb/typings/replay/canvas/webgl.d.ts | 9 -- packages/rrweb/typings/replay/index.d.ts | 73 --------------- packages/rrweb/typings/replay/machine.d.ts | 80 ---------------- .../rrweb/typings/replay/smoothscroll.d.ts | 1 - .../typings/replay/styles/inject-style.d.ts | 2 - packages/rrweb/typings/replay/timer.d.ts | 18 ---- packages/rrweb/typings/rrdom/index.d.ts | 1 - packages/rrweb/typings/rrdom/tree-node.d.ts | 20 ---- packages/rrweb/typings/utils.d.ts | 42 --------- turbo.json | 2 +- 47 files changed, 3 insertions(+), 770 deletions(-) delete mode 100644 packages/rrweb-snapshot/typings/css.d.ts delete mode 100644 packages/rrweb-snapshot/typings/index.d.ts delete mode 100644 packages/rrweb-snapshot/typings/rebuild.d.ts delete mode 100644 packages/rrweb-snapshot/typings/snapshot.d.ts delete mode 100644 packages/rrweb-snapshot/typings/utils.d.ts delete mode 100644 packages/rrweb/typings/entries/all.d.ts delete mode 100644 packages/rrweb/typings/entries/record-pack.d.ts delete mode 100644 packages/rrweb/typings/entries/replay-unpack.d.ts delete mode 100644 packages/rrweb/typings/index.d.ts delete mode 100644 packages/rrweb/typings/packer/base.d.ts delete mode 100644 packages/rrweb/typings/packer/index.d.ts delete mode 100644 packages/rrweb/typings/packer/pack.d.ts delete mode 100644 packages/rrweb/typings/packer/unpack.d.ts delete mode 100644 packages/rrweb/typings/plugins/console/record/error-stack-parser.d.ts delete mode 100644 packages/rrweb/typings/plugins/console/record/index.d.ts delete mode 100644 packages/rrweb/typings/plugins/console/record/stringify.d.ts delete mode 100644 packages/rrweb/typings/plugins/console/replay/index.d.ts delete mode 100644 packages/rrweb/typings/plugins/sequential-id/record/index.d.ts delete mode 100644 packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts delete mode 100644 packages/rrweb/typings/record/iframe-manager.d.ts delete mode 100644 packages/rrweb/typings/record/index.d.ts delete mode 100644 packages/rrweb/typings/record/mutation.d.ts delete mode 100644 packages/rrweb/typings/record/observer.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/2d.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/canvas.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/webgl.d.ts delete mode 100644 packages/rrweb/typings/record/observers/canvas/webgl2.d.ts delete mode 100644 packages/rrweb/typings/record/shadow-dom-manager.d.ts delete mode 100644 packages/rrweb/typings/record/stylesheet-manager.d.ts delete mode 100644 packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts delete mode 100644 packages/rrweb/typings/replay/canvas/2d.d.ts delete mode 100644 packages/rrweb/typings/replay/canvas/deserialize-args.d.ts delete mode 100644 packages/rrweb/typings/replay/canvas/index.d.ts delete mode 100644 packages/rrweb/typings/replay/canvas/webgl.d.ts delete mode 100644 packages/rrweb/typings/replay/index.d.ts delete mode 100644 packages/rrweb/typings/replay/machine.d.ts delete mode 100644 packages/rrweb/typings/replay/smoothscroll.d.ts delete mode 100644 packages/rrweb/typings/replay/styles/inject-style.d.ts delete mode 100644 packages/rrweb/typings/replay/timer.d.ts delete mode 100644 packages/rrweb/typings/rrdom/index.d.ts delete mode 100644 packages/rrweb/typings/rrdom/tree-node.d.ts delete mode 100644 packages/rrweb/typings/utils.d.ts diff --git a/packages/rrweb-snapshot/.gitignore b/packages/rrweb-snapshot/.gitignore index 3365d49249..639e5dd119 100644 --- a/packages/rrweb-snapshot/.gitignore +++ b/packages/rrweb-snapshot/.gitignore @@ -6,3 +6,4 @@ dist es lib temp +typings diff --git a/packages/rrweb-snapshot/typings/css.d.ts b/packages/rrweb-snapshot/typings/css.d.ts deleted file mode 100644 index 6207385ab7..0000000000 --- a/packages/rrweb-snapshot/typings/css.d.ts +++ /dev/null @@ -1,92 +0,0 @@ -export interface ParserOptions { - silent?: boolean; - source?: string; -} -export interface ParserError { - message?: string; - reason?: string; - filename?: string; - line?: number; - column?: number; - source?: string; -} -export interface Loc { - line?: number; - column?: number; -} -export interface Node { - type?: string; - parent?: Node; - position?: { - start?: Loc; - end?: Loc; - source?: string; - content?: string; - }; -} -export interface Rule extends Node { - selectors?: string[]; - declarations?: Array; -} -export interface Declaration extends Node { - property?: string; - value?: string; -} -export interface Comment extends Node { - comment?: string; -} -export interface Charset extends Node { - charset?: string; -} -export interface CustomMedia extends Node { - name?: string; - media?: string; -} -export interface Document extends Node { - document?: string; - vendor?: string; - rules?: Array; -} -export interface FontFace extends Node { - declarations?: Array; -} -export interface Host extends Node { - rules?: Array; -} -export interface Import extends Node { - import?: string; -} -export interface KeyFrames extends Node { - name?: string; - vendor?: string; - keyframes?: Array; -} -export interface KeyFrame extends Node { - values?: string[]; - declarations?: Array; -} -export interface Media extends Node { - media?: string; - rules?: Array; -} -export interface Namespace extends Node { - namespace?: string; -} -export interface Page extends Node { - selectors?: string[]; - declarations?: Array; -} -export interface Supports extends Node { - supports?: string; - rules?: Array; -} -export declare type AtRule = Charset | CustomMedia | Document | FontFace | Host | Import | KeyFrames | Media | Namespace | Page | Supports; -export interface StyleRules { - source?: string; - rules: Array; - parsingErrors?: ParserError[]; -} -export interface Stylesheet extends Node { - stylesheet?: StyleRules; -} -export declare function parse(css: string, options?: ParserOptions): Stylesheet; diff --git a/packages/rrweb-snapshot/typings/index.d.ts b/packages/rrweb-snapshot/typings/index.d.ts deleted file mode 100644 index 194b8d3ad7..0000000000 --- a/packages/rrweb-snapshot/typings/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import snapshot, { serializeNodeWithId, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, classMatchesRegex, IGNORED_NODE } from './snapshot'; -import rebuild, { buildNodeWithSN, addHoverClass, createCache } from './rebuild'; -export * from './types'; -export * from './utils'; -export { snapshot, serializeNodeWithId, rebuild, buildNodeWithSN, addHoverClass, createCache, transformAttribute, visitSnapshot, cleanupSnapshot, needMaskingText, classMatchesRegex, IGNORED_NODE, }; diff --git a/packages/rrweb-snapshot/typings/rebuild.d.ts b/packages/rrweb-snapshot/typings/rebuild.d.ts deleted file mode 100644 index 894149e2bb..0000000000 --- a/packages/rrweb-snapshot/typings/rebuild.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { serializedNodeWithId, BuildCache } from './types'; -import { Mirror } from './utils'; -export declare function addHoverClass(cssText: string, cache: BuildCache): string; -export declare function createCache(): BuildCache; -export declare function buildNodeWithSN(n: serializedNodeWithId, options: { - doc: Document; - mirror: Mirror; - skipChild?: boolean; - hackCss: boolean; - afterAppend?: (n: Node) => unknown; - cache: BuildCache; -}): Node | null; -declare function rebuild(n: serializedNodeWithId, options: { - doc: Document; - onVisit?: (node: Node) => unknown; - hackCss?: boolean; - afterAppend?: (n: Node) => unknown; - cache: BuildCache; - mirror: Mirror; -}): Node | null; -export default rebuild; diff --git a/packages/rrweb-snapshot/typings/snapshot.d.ts b/packages/rrweb-snapshot/typings/snapshot.d.ts deleted file mode 100644 index da722cd397..0000000000 --- a/packages/rrweb-snapshot/typings/snapshot.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { serializedNodeWithId, MaskInputOptions, SlimDOMOptions, DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn, serializedElementNodeWithId } from './types'; -import { Mirror } from './utils'; -export declare const IGNORED_NODE = -2; -export declare function absoluteToStylesheet(cssText: string | null, href: string): string; -export declare function absoluteToDoc(doc: Document, attributeValue: string): string; -export declare function transformAttribute(doc: Document, tagName: string, name: string, value: string): string; -export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null): boolean; -export declare function classMatchesRegex(node: Node | null, regex: RegExp, checkAncestors: boolean): boolean; -export declare function needMaskingText(node: Node, maskTextClass: string | RegExp, maskTextSelector: string | null): boolean; -export declare function serializeNodeWithId(n: Node, options: { - doc: Document; - mirror: Mirror; - blockClass: string | RegExp; - blockSelector: string | null; - maskTextClass: string | RegExp; - maskTextSelector: string | null; - skipChild: boolean; - inlineStylesheet: boolean; - newlyAddedElement?: boolean; - maskInputOptions?: MaskInputOptions; - maskTextFn: MaskTextFn | undefined; - maskInputFn: MaskInputFn | undefined; - slimDOMOptions: SlimDOMOptions; - dataURLOptions?: DataURLOptions; - keepIframeSrcFn?: KeepIframeSrcFn; - inlineImages?: boolean; - recordCanvas?: boolean; - preserveWhiteSpace?: boolean; - onSerialize?: (n: Node) => unknown; - onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedElementNodeWithId) => unknown; - iframeLoadTimeout?: number; - onStylesheetLoad?: (linkNode: HTMLLinkElement, node: serializedElementNodeWithId) => unknown; - stylesheetLoadTimeout?: number; -}): serializedNodeWithId | null; -declare function snapshot(n: Document, options?: { - mirror?: Mirror; - blockClass?: string | RegExp; - blockSelector?: string | null; - maskTextClass?: string | RegExp; - maskTextSelector?: string | null; - inlineStylesheet?: boolean; - maskAllInputs?: boolean | MaskInputOptions; - maskTextFn?: MaskTextFn; - maskInputFn?: MaskTextFn; - slimDOM?: boolean | SlimDOMOptions; - dataURLOptions?: DataURLOptions; - inlineImages?: boolean; - recordCanvas?: boolean; - preserveWhiteSpace?: boolean; - onSerialize?: (n: Node) => unknown; - onIframeLoad?: (iframeNode: HTMLIFrameElement, node: serializedElementNodeWithId) => unknown; - iframeLoadTimeout?: number; - onStylesheetLoad?: (linkNode: HTMLLinkElement, node: serializedElementNodeWithId) => unknown; - stylesheetLoadTimeout?: number; - keepIframeSrcFn?: KeepIframeSrcFn; -}): serializedNodeWithId | null; -export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void; -export declare function cleanupSnapshot(): void; -export default snapshot; diff --git a/packages/rrweb-snapshot/typings/utils.d.ts b/packages/rrweb-snapshot/typings/utils.d.ts deleted file mode 100644 index 0a793b1df4..0000000000 --- a/packages/rrweb-snapshot/typings/utils.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MaskInputFn, MaskInputOptions, IMirror, serializedNodeWithId } from './types'; -export declare function isElement(n: Node): n is Element; -export declare function isShadowRoot(n: Node): n is ShadowRoot; -export declare function isNativeShadowDom(shadowRoot: ShadowRoot): boolean; -export declare class Mirror implements IMirror { - private idNodeMap; - private nodeMetaMap; - getId(n: Node | undefined | null): number; - getNode(id: number): Node | null; - getIds(): number[]; - getMeta(n: Node): serializedNodeWithId | null; - removeNodeFromMap(n: Node): void; - has(id: number): boolean; - hasNode(node: Node): boolean; - add(n: Node, meta: serializedNodeWithId): void; - replace(id: number, n: Node): void; - reset(): void; -} -export declare function createMirror(): Mirror; -export declare function maskInputValue({ maskInputOptions, tagName, type, value, maskInputFn, }: { - maskInputOptions: MaskInputOptions; - tagName: string; - type: string | number | boolean | null; - value: string | null; - maskInputFn?: MaskInputFn; -}): string; -export declare function is2DCanvasBlank(canvas: HTMLCanvasElement): boolean; diff --git a/packages/rrweb/.gitignore b/packages/rrweb/.gitignore index 4875c32f5c..42374e6eb1 100644 --- a/packages/rrweb/.gitignore +++ b/packages/rrweb/.gitignore @@ -7,6 +7,7 @@ build dist es lib +typings temp diff --git a/packages/rrweb/typings/entries/all.d.ts b/packages/rrweb/typings/entries/all.d.ts deleted file mode 100644 index d67ff92447..0000000000 --- a/packages/rrweb/typings/entries/all.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from '../index'; -export * from '../packer'; -export * from '../plugins/console/record'; -export * from '../plugins/console/replay'; diff --git a/packages/rrweb/typings/entries/record-pack.d.ts b/packages/rrweb/typings/entries/record-pack.d.ts deleted file mode 100644 index c0ea472f90..0000000000 --- a/packages/rrweb/typings/entries/record-pack.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '../record/index'; -export * from '../packer/pack'; diff --git a/packages/rrweb/typings/entries/replay-unpack.d.ts b/packages/rrweb/typings/entries/replay-unpack.d.ts deleted file mode 100644 index 5789d90285..0000000000 --- a/packages/rrweb/typings/entries/replay-unpack.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '../replay'; -export * from '../packer/unpack'; diff --git a/packages/rrweb/typings/index.d.ts b/packages/rrweb/typings/index.d.ts deleted file mode 100644 index 6b6d18d12f..0000000000 --- a/packages/rrweb/typings/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import record from './record'; -import { Replayer } from './replay'; -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, }; diff --git a/packages/rrweb/typings/packer/base.d.ts b/packages/rrweb/typings/packer/base.d.ts deleted file mode 100644 index 77d6837045..0000000000 --- a/packages/rrweb/typings/packer/base.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { eventWithTime } from '../types'; -export declare type PackFn = (event: eventWithTime) => string; -export declare type UnpackFn = (raw: string) => eventWithTime; -export declare type eventWithTimeAndPacker = eventWithTime & { - v: string; -}; -export declare const MARK = "v1"; diff --git a/packages/rrweb/typings/packer/index.d.ts b/packages/rrweb/typings/packer/index.d.ts deleted file mode 100644 index beca5f61fb..0000000000 --- a/packages/rrweb/typings/packer/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { pack } from './pack'; -export { unpack } from './unpack'; diff --git a/packages/rrweb/typings/packer/pack.d.ts b/packages/rrweb/typings/packer/pack.d.ts deleted file mode 100644 index da24e925c6..0000000000 --- a/packages/rrweb/typings/packer/pack.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { PackFn } from './base'; -export declare const pack: PackFn; diff --git a/packages/rrweb/typings/packer/unpack.d.ts b/packages/rrweb/typings/packer/unpack.d.ts deleted file mode 100644 index 002c745b12..0000000000 --- a/packages/rrweb/typings/packer/unpack.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { UnpackFn } from './base'; -export declare const unpack: UnpackFn; diff --git a/packages/rrweb/typings/plugins/console/record/error-stack-parser.d.ts b/packages/rrweb/typings/plugins/console/record/error-stack-parser.d.ts deleted file mode 100644 index 86a961da9b..0000000000 --- a/packages/rrweb/typings/plugins/console/record/error-stack-parser.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -export declare class StackFrame { - private fileName; - private functionName; - private lineNumber?; - private columnNumber?; - constructor(obj: { - fileName?: string; - functionName?: string; - lineNumber?: number; - columnNumber?: number; - }); - toString(): string; -} -export declare const ErrorStackParser: { - parse: (error: Error) => StackFrame[]; - extractLocation: (urlLike: string) => (string | undefined)[]; - parseV8OrIE: (error: { - stack: string; - }) => StackFrame[]; - parseFFOrSafari: (error: { - stack: string; - }) => StackFrame[]; - parseOpera: (e: { - stacktrace?: string; - message: string; - stack?: string; - }) => StackFrame[]; - parseOpera9: (e: { - message: string; - }) => StackFrame[]; - parseOpera10: (e: { - stacktrace: string; - }) => StackFrame[]; - parseOpera11: (error: { - stack: string; - }) => StackFrame[]; -}; diff --git a/packages/rrweb/typings/plugins/console/record/index.d.ts b/packages/rrweb/typings/plugins/console/record/index.d.ts deleted file mode 100644 index 9b3b8de44d..0000000000 --- a/packages/rrweb/typings/plugins/console/record/index.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { RecordPlugin } from '../../../types'; -export declare type StringifyOptions = { - stringLengthLimit?: number; - numOfKeysLimit: number; - depthOfLimit: number; -}; -declare type LogRecordOptions = { - level?: LogLevel[]; - lengthThreshold?: number; - stringifyOptions?: StringifyOptions; - logger?: Logger | 'console'; -}; -export declare type LogData = { - level: LogLevel; - trace: string[]; - payload: string[]; -}; -export declare type Logger = { - assert?: typeof console.assert; - clear?: typeof console.clear; - count?: typeof console.count; - countReset?: typeof console.countReset; - debug?: typeof console.debug; - dir?: typeof console.dir; - dirxml?: typeof console.dirxml; - error?: typeof console.error; - group?: typeof console.group; - groupCollapsed?: typeof console.groupCollapsed; - groupEnd?: () => void; - info?: typeof console.info; - log?: typeof console.log; - table?: typeof console.table; - time?: typeof console.time; - timeEnd?: typeof console.timeEnd; - timeLog?: typeof console.timeLog; - 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/plugins/console/record/stringify.d.ts b/packages/rrweb/typings/plugins/console/record/stringify.d.ts deleted file mode 100644 index 0c61d90195..0000000000 --- a/packages/rrweb/typings/plugins/console/record/stringify.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import type { StringifyOptions } from './index'; -export declare function stringify(obj: unknown, stringifyOptions?: StringifyOptions): string; diff --git a/packages/rrweb/typings/plugins/console/replay/index.d.ts b/packages/rrweb/typings/plugins/console/replay/index.d.ts deleted file mode 100644 index 823560cc9c..0000000000 --- a/packages/rrweb/typings/plugins/console/replay/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { LogLevel, LogData } from '../record'; -import { ReplayPlugin } from '../../../types'; -declare type ReplayLogger = Partial void>>; -declare type LogReplayConfig = { - level?: LogLevel[]; - replayLogger?: ReplayLogger; -}; -export declare const getReplayConsolePlugin: (options?: LogReplayConfig) => ReplayPlugin; -export {}; diff --git a/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts b/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts deleted file mode 100644 index a2f86c3cee..0000000000 --- a/packages/rrweb/typings/plugins/sequential-id/record/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { RecordPlugin } from '../../../types'; -export declare type SequentialIdOptions = { - key: string; -}; -export declare const PLUGIN_NAME = "rrweb/sequential-id@1"; -export declare const getRecordSequentialIdPlugin: (options?: Partial) => RecordPlugin; diff --git a/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts b/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts deleted file mode 100644 index a8f7e80c0e..0000000000 --- a/packages/rrweb/typings/plugins/sequential-id/replay/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { SequentialIdOptions } from '../record'; -import type { ReplayPlugin } from '../../../types'; -declare type Options = SequentialIdOptions & { - warnOnMissingId: boolean; -}; -export declare const getReplaySequentialIdPlugin: (options?: Partial) => ReplayPlugin; -export {}; diff --git a/packages/rrweb/typings/record/iframe-manager.d.ts b/packages/rrweb/typings/record/iframe-manager.d.ts deleted file mode 100644 index 4dc8c120b3..0000000000 --- a/packages/rrweb/typings/record/iframe-manager.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import type { mutationCallBack } from '../types'; -export declare class IframeManager { - private iframes; - private mutationCb; - private loadListener?; - constructor(options: { - mutationCb: mutationCallBack; - }); - addIframe(iframeEl: HTMLIFrameElement): void; - addLoadListener(cb: (iframeEl: HTMLIFrameElement) => unknown): void; - attachIframe(iframeEl: HTMLIFrameElement, childSn: serializedNodeWithId, mirror: Mirror): void; -} diff --git a/packages/rrweb/typings/record/index.d.ts b/packages/rrweb/typings/record/index.d.ts deleted file mode 100644 index f997da1f6f..0000000000 --- a/packages/rrweb/typings/record/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { eventWithTime, recordOptions, listenerHandler } from '../types'; -declare function record(options?: recordOptions): listenerHandler | undefined; -declare namespace record { - var addCustomEvent: (tag: string, payload: T) => void; - var freezePage: () => void; - var takeFullSnapshot: (isCheckout?: boolean | undefined) => void; - var mirror: import("rrweb-snapshot").Mirror; -} -export default record; diff --git a/packages/rrweb/typings/record/mutation.d.ts b/packages/rrweb/typings/record/mutation.d.ts deleted file mode 100644 index e554eabe1e..0000000000 --- a/packages/rrweb/typings/record/mutation.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { mutationRecord, MutationBufferParam } from '../types'; -export default class MutationBuffer { - private frozen; - private locked; - private texts; - private attributes; - private removes; - private mapRemoves; - private movedMap; - private addedSet; - private movedSet; - private droppedSet; - private mutationCb; - private blockClass; - private blockSelector; - private maskTextClass; - private maskTextSelector; - private inlineStylesheet; - private maskInputOptions; - private maskTextFn; - private maskInputFn; - private recordCanvas; - private inlineImages; - private slimDOMOptions; - private doc; - private mirror; - private iframeManager; - private stylesheetManager; - private shadowDomManager; - private canvasManager; - init(options: MutationBufferParam): void; - freeze(): void; - unfreeze(): void; - isFrozen(): boolean; - lock(): void; - unlock(): void; - reset(): void; - processMutations: (mutations: mutationRecord[]) => void; - emit: () => void; - private processMutation; - private genAdds; -} diff --git a/packages/rrweb/typings/record/observer.d.ts b/packages/rrweb/typings/record/observer.d.ts deleted file mode 100644 index 86453a1465..0000000000 --- a/packages/rrweb/typings/record/observer.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { observerParam, listenerHandler, hooksParam, MutationBufferParam } from '../types'; -import MutationBuffer from './mutation'; -export declare const mutationBuffers: MutationBuffer[]; -export declare function initMutationObserver(options: MutationBufferParam, rootEl: Node): MutationObserver; -export declare function initScrollObserver({ scrollCb, doc, mirror, blockClass, sampling, }: Pick): listenerHandler; -export declare const INPUT_TAGS: string[]; -export declare function initObservers(o: observerParam, hooks?: hooksParam): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/2d.d.ts b/packages/rrweb/typings/record/observers/canvas/2d.d.ts deleted file mode 100644 index febe6b226d..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/2d.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { Mirror } from 'rrweb-snapshot'; -import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; -export default function initCanvas2DMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts deleted file mode 100644 index d0ba11718f..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/canvas-manager.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { Mirror } from 'rrweb-snapshot'; -import type { blockClass, canvasMutationCallback, IWindow } from '../../../types'; -export declare type RafStamps = { - latestId: number; - invokeId: number | null; -}; -export declare class CanvasManager { - private pendingCanvasMutations; - private rafStamps; - private mirror; - private mutationCb; - private resetObservers?; - private frozen; - private locked; - reset(): void; - freeze(): void; - unfreeze(): void; - lock(): void; - unlock(): void; - constructor(options: { - recordCanvas: boolean; - mutationCb: canvasMutationCallback; - win: IWindow; - blockClass: blockClass; - mirror: Mirror; - sampling?: 'all' | number; - }); - private processMutation; - private initCanvasFPSObserver; - private initCanvasMutationObserver; - private startPendingCanvasMutationFlusher; - private startRAFTimestamping; - flushPendingCanvasMutations(): void; - flushPendingCanvasMutationFor(canvas: HTMLCanvasElement, id: number): void; -} diff --git a/packages/rrweb/typings/record/observers/canvas/canvas.d.ts b/packages/rrweb/typings/record/observers/canvas/canvas.d.ts deleted file mode 100644 index c35e97aa55..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/canvas.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import type { blockClass, IWindow, listenerHandler } from '../../../types'; -export default function initCanvasContextObserver(win: IWindow, blockClass: blockClass): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts b/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts deleted file mode 100644 index c60230fd15..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/serialize-args.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { IWindow, CanvasArg } from '../../../types'; -export declare function variableListFor(ctx: RenderingContext, ctor: string): unknown[]; -export declare const saveWebGLVar: (value: unknown, win: IWindow, ctx: RenderingContext) => number | void; -export declare function serializeArg(value: unknown, win: IWindow, ctx: RenderingContext): CanvasArg; -export declare const serializeArgs: (args: Array, win: IWindow, ctx: RenderingContext) => CanvasArg[]; -export declare const isInstanceOfWebGLObject: (value: unknown, win: IWindow) => value is WebGLTexture | WebGLShader | WebGLBuffer | WebGLVertexArrayObject | WebGLProgram | WebGLActiveInfo | WebGLUniformLocation | WebGLFramebuffer | WebGLRenderbuffer | WebGLShaderPrecisionFormat; diff --git a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts b/packages/rrweb/typings/record/observers/canvas/webgl.d.ts deleted file mode 100644 index f5bceaeb22..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/webgl.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { Mirror } from 'rrweb-snapshot'; -import { blockClass, canvasManagerMutationCallback, IWindow, listenerHandler } from '../../../types'; -export default function initCanvasWebGLMutationObserver(cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/observers/canvas/webgl2.d.ts b/packages/rrweb/typings/record/observers/canvas/webgl2.d.ts deleted file mode 100644 index c1263a8f58..0000000000 --- a/packages/rrweb/typings/record/observers/canvas/webgl2.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import { blockClass, canvasMutationCallback, IWindow, listenerHandler, Mirror } from '../../../types'; -export default function initCanvasWebGLMutationObserver(cb: canvasMutationCallback, win: IWindow, blockClass: blockClass, mirror: Mirror): listenerHandler; diff --git a/packages/rrweb/typings/record/shadow-dom-manager.d.ts b/packages/rrweb/typings/record/shadow-dom-manager.d.ts deleted file mode 100644 index bf6a306c8f..0000000000 --- a/packages/rrweb/typings/record/shadow-dom-manager.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { mutationCallBack, scrollCallback, MutationBufferParam, SamplingStrategy } from '../types'; -import type { Mirror } from 'rrweb-snapshot'; -declare type BypassOptions = Omit & { - sampling: SamplingStrategy; -}; -export declare class ShadowDomManager { - private mutationCb; - private scrollCb; - private bypassOptions; - private mirror; - private restorePatches; - constructor(options: { - mutationCb: mutationCallBack; - scrollCb: scrollCallback; - bypassOptions: BypassOptions; - mirror: Mirror; - }); - addShadowRoot(shadowRoot: ShadowRoot, doc: Document): void; - observeAttachShadow(iframeElement: HTMLIFrameElement): void; - reset(): void; -} -export {}; diff --git a/packages/rrweb/typings/record/stylesheet-manager.d.ts b/packages/rrweb/typings/record/stylesheet-manager.d.ts deleted file mode 100644 index 022fc54445..0000000000 --- a/packages/rrweb/typings/record/stylesheet-manager.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import type { mutationCallBack } from '../types'; -export declare class StylesheetManager { - private trackedStylesheets; - private mutationCb; - constructor(options: { - mutationCb: mutationCallBack; - }); - addStylesheet(linkEl: HTMLLinkElement): void; - private trackStylesheet; - attachStylesheet(linkEl: HTMLLinkElement, childSn: serializedNodeWithId, mirror: Mirror): void; -} diff --git a/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts b/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts deleted file mode 100644 index ca58dc6b17..0000000000 --- a/packages/rrweb/typings/record/workers/image-bitmap-data-url-worker.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse } from '../../types'; -export interface ImageBitmapDataURLRequestWorker { - postMessage: (message: ImageBitmapDataURLWorkerParams, transfer?: [ImageBitmap]) => void; - onmessage: (message: MessageEvent) => void; -} diff --git a/packages/rrweb/typings/replay/canvas/2d.d.ts b/packages/rrweb/typings/replay/canvas/2d.d.ts deleted file mode 100644 index ca7485046a..0000000000 --- a/packages/rrweb/typings/replay/canvas/2d.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Replayer } from '../'; -import type { canvasMutationCommand } from '../../types'; -export default function canvasMutation({ event, mutation, target, imageMap, errorHandler, }: { - event: Parameters[0]; - mutation: canvasMutationCommand; - target: HTMLCanvasElement; - imageMap: Replayer['imageMap']; - errorHandler: Replayer['warnCanvasMutationFailed']; -}): Promise; diff --git a/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts b/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts deleted file mode 100644 index dd7d5a6268..0000000000 --- a/packages/rrweb/typings/replay/canvas/deserialize-args.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Replayer } from '../'; -import type { CanvasArg, SerializedCanvasArg } from '../../types'; -export declare function variableListFor(ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext, ctor: string): any[]; -export declare function isSerializedArg(arg: unknown): arg is SerializedCanvasArg; -export declare function deserializeArg(imageMap: Replayer['imageMap'], ctx: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext | null, preload?: { - isUnchanged: boolean; -}): (arg: CanvasArg) => Promise; diff --git a/packages/rrweb/typings/replay/canvas/index.d.ts b/packages/rrweb/typings/replay/canvas/index.d.ts deleted file mode 100644 index e316e3ea51..0000000000 --- a/packages/rrweb/typings/replay/canvas/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Replayer } from '..'; -import { canvasMutationData } from '../../types'; -export default function canvasMutation({ event, mutation, target, imageMap, canvasEventMap, errorHandler, }: { - event: Parameters[0]; - mutation: canvasMutationData; - target: HTMLCanvasElement; - imageMap: Replayer['imageMap']; - canvasEventMap: Replayer['canvasEventMap']; - errorHandler: Replayer['warnCanvasMutationFailed']; -}): Promise; diff --git a/packages/rrweb/typings/replay/canvas/webgl.d.ts b/packages/rrweb/typings/replay/canvas/webgl.d.ts deleted file mode 100644 index 732b35a991..0000000000 --- a/packages/rrweb/typings/replay/canvas/webgl.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Replayer } from '../'; -import { CanvasContext, canvasMutationCommand } from '../../types'; -export default function webglMutation({ mutation, target, type, imageMap, errorHandler, }: { - mutation: canvasMutationCommand; - target: HTMLCanvasElement; - type: CanvasContext; - imageMap: Replayer['imageMap']; - errorHandler: Replayer['warnCanvasMutationFailed']; -}): Promise; diff --git a/packages/rrweb/typings/replay/index.d.ts b/packages/rrweb/typings/replay/index.d.ts deleted file mode 100644 index 409f2dc15c..0000000000 --- a/packages/rrweb/typings/replay/index.d.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Mirror } from 'rrweb-snapshot'; -import { RRDocument } from 'rrdom'; -import { Timer } from './timer'; -import { createPlayerService, createSpeedService } from './machine'; -import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types'; -import './styles/style.css'; -export declare class Replayer { - wrapper: HTMLDivElement; - iframe: HTMLIFrameElement; - service: ReturnType; - speedService: ReturnType; - get timer(): Timer; - config: playerConfig; - usingVirtualDom: boolean; - virtualDom: RRDocument; - private mouse; - private mouseTail; - private tailPositions; - private emitter; - private nextUserInteractionEvent; - private legacy_missingNodeRetryMap; - private cache; - private imageMap; - private canvasEventMap; - private mirror; - private firstFullSnapshot; - private newDocumentQueue; - private mousePos; - private touchActive; - 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; - getTimeOffset(): number; - getMirror(): Mirror; - play(timeOffset?: number): void; - pause(timeOffset?: number): void; - resume(timeOffset?: number): void; - startLive(baselineTime?: number): void; - addEvent(rawEvent: eventWithTime | string): void; - enableInteract(): void; - disableInteract(): void; - resetCache(): void; - private setupDom; - private handleResize; - private applyEventsSynchronously; - private getCastFn; - private rebuildFullSnapshot; - private insertStyleRules; - private attachDocumentToIframe; - private collectIframeAndAttachDocument; - private waitForStylesheetLoad; - private preloadAllImages; - private preloadImages; - private deserializeAndPreloadCanvasEvents; - private applyIncremental; - private applyMutation; - private applyScroll; - private applyInput; - private legacy_resolveMissingNode; - private moveAndHover; - private drawMouseTail; - private hoverElements; - private isUserInteraction; - private backToNormal; - private warnNodeNotFound; - private warnCanvasMutationFailed; - private debugNodeNotFound; - private warn; - private debug; -} diff --git a/packages/rrweb/typings/replay/machine.d.ts b/packages/rrweb/typings/replay/machine.d.ts deleted file mode 100644 index 5331d0092c..0000000000 --- a/packages/rrweb/typings/replay/machine.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { StateMachine } from '@xstate/fsm'; -import { playerConfig, eventWithTime, Emitter } from '../types'; -import { Timer } from './timer'; -export declare type PlayerContext = { - events: eventWithTime[]; - timer: Timer; - timeOffset: number; - baselineTime: number; - lastPlayedEvent: eventWithTime | null; -}; -export declare type PlayerEvent = { - type: 'PLAY'; - payload: { - timeOffset: number; - }; -} | { - type: 'CAST_EVENT'; - payload: { - event: eventWithTime; - }; -} | { - type: 'PAUSE'; -} | { - type: 'TO_LIVE'; - payload: { - baselineTime?: number; - }; -} | { - type: 'ADD_EVENT'; - payload: { - event: eventWithTime; - }; -} | { - type: 'END'; -}; -export declare type PlayerState = { - value: 'playing'; - context: PlayerContext; -} | { - value: 'paused'; - context: PlayerContext; -} | { - value: 'live'; - context: PlayerContext; -}; -export declare function discardPriorSnapshots(events: eventWithTime[], baselineTime: number): eventWithTime[]; -declare type PlayerAssets = { - emitter: Emitter; - applyEventsSynchronously(events: Array): void; - getCastFn(event: eventWithTime, isSync: boolean): () => void; -}; -export declare function createPlayerService(context: PlayerContext, { getCastFn, applyEventsSynchronously, emitter }: PlayerAssets): StateMachine.Service; -export declare type SpeedContext = { - normalSpeed: playerConfig['speed']; - timer: Timer; -}; -export declare type SpeedEvent = { - type: 'FAST_FORWARD'; - payload: { - speed: playerConfig['speed']; - }; -} | { - type: 'BACK_TO_NORMAL'; -} | { - type: 'SET_SPEED'; - payload: { - speed: playerConfig['speed']; - }; -}; -export declare type SpeedState = { - value: 'normal'; - context: SpeedContext; -} | { - value: 'skipping'; - context: SpeedContext; -}; -export declare function createSpeedService(context: SpeedContext): StateMachine.Service; -export declare type PlayerMachineState = StateMachine.State; -export declare type SpeedMachineState = StateMachine.State; -export {}; diff --git a/packages/rrweb/typings/replay/smoothscroll.d.ts b/packages/rrweb/typings/replay/smoothscroll.d.ts deleted file mode 100644 index 562e3df17c..0000000000 --- a/packages/rrweb/typings/replay/smoothscroll.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function polyfill(w?: Window, d?: Document): void; diff --git a/packages/rrweb/typings/replay/styles/inject-style.d.ts b/packages/rrweb/typings/replay/styles/inject-style.d.ts deleted file mode 100644 index 88c0f362d7..0000000000 --- a/packages/rrweb/typings/replay/styles/inject-style.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const rules: (blockClass: string) => string[]; -export default rules; diff --git a/packages/rrweb/typings/replay/timer.d.ts b/packages/rrweb/typings/replay/timer.d.ts deleted file mode 100644 index 376c92495b..0000000000 --- a/packages/rrweb/typings/replay/timer.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { actionWithDelay, eventWithTime } from '../types'; -export declare class Timer { - timeOffset: number; - speed: number; - private actions; - private raf; - private liveMode; - constructor(actions: actionWithDelay[] | undefined, speed: number); - addAction(action: actionWithDelay): void; - addActions(actions: actionWithDelay[]): void; - start(): void; - clear(): void; - setSpeed(speed: number): void; - toggleLiveMode(mode: boolean): void; - isActive(): boolean; - private findActionIndex; -} -export declare function addDelay(event: eventWithTime, baselineTime: number): number; diff --git a/packages/rrweb/typings/rrdom/index.d.ts b/packages/rrweb/typings/rrdom/index.d.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/packages/rrweb/typings/rrdom/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/rrweb/typings/rrdom/tree-node.d.ts b/packages/rrweb/typings/rrdom/tree-node.d.ts deleted file mode 100644 index be3d9635a6..0000000000 --- a/packages/rrweb/typings/rrdom/tree-node.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export declare type AnyObject = { - [key: string]: any; - __rrdom__?: RRdomTreeNode; -}; -export declare class RRdomTreeNode implements AnyObject { - parent: AnyObject | null; - previousSibling: AnyObject | null; - nextSibling: AnyObject | null; - firstChild: AnyObject | null; - lastChild: AnyObject | null; - childrenVersion: number; - childIndexCachedUpTo: AnyObject | null; - cachedIndex: number; - cachedIndexVersion: number; - get isAttached(): boolean; - get hasChildren(): boolean; - childrenChanged(): void; - getCachedIndex(parentNode: AnyObject): number; - setCachedIndex(parentNode: AnyObject, index: number): void; -} diff --git a/packages/rrweb/typings/utils.d.ts b/packages/rrweb/typings/utils.d.ts deleted file mode 100644 index 106f14c131..0000000000 --- a/packages/rrweb/typings/utils.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types'; -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): (...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; -}, name: string, replacement: (...args: unknown[]) => unknown): () => void; -export declare function getWindowHeight(): number; -export declare function getWindowWidth(): number; -export declare function isBlocked(node: Node | null, blockClass: blockClass, checkAncestors: boolean): boolean; -export declare function isSerialized(n: Node, mirror: Mirror): boolean; -export declare function isIgnored(n: Node, mirror: Mirror): boolean; -export declare function isAncestorRemoved(target: Node, mirror: Mirror): boolean; -export declare function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent; -export declare function polyfill(win?: Window & typeof globalThis): void; -declare type ResolveTree = { - value: addedNodeMutation; - children: ResolveTree[]; - parent: ResolveTree | null; -}; -export declare function queueToResolveTrees(queue: addedNodeMutation[]): ResolveTree[]; -export declare function iterateResolveTree(tree: ResolveTree, cb: (mutation: addedNodeMutation) => unknown): void; -export declare type AppendedIframe = { - mutationInQueue: addedNodeMutation; - builtNode: HTMLIFrameElement | RRIFrameElement; -}; -export declare function isSerializedIframe(n: TNode, mirror: IMirror): boolean; -export declare function isSerializedStylesheet(n: TNode, mirror: IMirror): boolean; -export declare function getBaseDimension(node: Node, rootIframe: Node): DocumentDimension; -export declare function hasShadowRoot(n: T): n is T & { - shadowRoot: ShadowRoot; -}; -export declare function getNestedRule(rules: CSSRuleList, position: number[]): CSSGroupingRule; -export declare function getPositionsAndIndex(nestedIndex: number[]): { - positions: number[]; - index: number | undefined; -}; -export declare function uniqueTextMutations(mutations: textMutation[]): textMutation[]; -export {}; diff --git a/turbo.json b/turbo.json index cf58a139e7..3908cac7ef 100644 --- a/turbo.json +++ b/turbo.json @@ -4,7 +4,7 @@ "pipeline": { "prepublish": { "dependsOn": ["^prepublish"], - "outputs": ["lib/**", "es/**", "dist/**"] + "outputs": ["lib/**", "es/**", "dist/**", "typings/**"] }, "test": {}, "test:watch": {}, From f1b23ddcccb1e1990b570abb8650afb95d4250db Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 31 Jul 2022 09:01:04 +0800 Subject: [PATCH 037/201] fix: canvas data in iframe wasn't applied in the fast-forward mode (#944) * fix: canvas data in iframe wasn't applied in the fastforward mode * add more comments * Update packages/rrdom/src/diff.ts Co-authored-by: Justin Halsall * apply Juice10's suggestion Co-authored-by: Justin Halsall --- packages/rrdom/src/diff.ts | 19 +- packages/rrdom/src/index.ts | 1 + packages/rrweb-snapshot/src/rebuild.ts | 9 +- .../rrweb/test/events/canvas-in-iframe.ts | 181 ++++++++++++++++++ packages/rrweb/test/replayer.test.ts | 26 +++ 5 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 packages/rrweb/test/events/canvas-in-iframe.ts diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index b3a356d980..f0896088a9 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -136,14 +136,27 @@ export function diff( break; } case 'CANVAS': - (newTree as RRCanvasElement).canvasMutations.forEach( - (canvasMutation) => + { + const rrCanvasElement = newTree as RRCanvasElement; + // This canvas element is created with initial data in an iframe element. https://github.com/rrweb-io/rrweb/pull/944 + if (rrCanvasElement.rr_dataURL !== null) { + const image = document.createElement('img'); + image.onload = () => { + const ctx = (oldElement as HTMLCanvasElement).getContext('2d'); + if (ctx) { + ctx.drawImage(image, 0, 0, image.width, image.height); + } + }; + image.src = rrCanvasElement.rr_dataURL; + } + rrCanvasElement.canvasMutations.forEach((canvasMutation) => replayer.applyCanvas( canvasMutation.event, canvasMutation.mutation, oldTree as HTMLCanvasElement, ), - ); + ); + } break; case 'STYLE': applyVirtualStyleRulesToNode( diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index 646dc127ac..16da3ed8b9 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -149,6 +149,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) { export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {} export class RRCanvasElement extends RRElement implements IRRElement { + public rr_dataURL: string | null = null; public canvasMutations: { event: canvasEventWithTime; mutation: canvasMutationData; diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 7eebc90125..60e3669b11 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -228,13 +228,20 @@ function buildNode( // handle internal attributes if (tagName === 'canvas' && name === 'rr_dataURL') { const image = document.createElement('img'); - image.src = value; image.onload = () => { const ctx = (node as HTMLCanvasElement).getContext('2d'); if (ctx) { ctx.drawImage(image, 0, 0, image.width, image.height); } }; + image.src = value; + type RRCanvasElement = { + RRNodeType: NodeType; + rr_dataURL: string; + }; + // If the canvas element is created in RRDom runtime (seeking to a time point), the canvas context isn't supported. So the data has to be stored and not handled until diff process. https://github.com/rrweb-io/rrweb/pull/944 + if (((node as unknown) as RRCanvasElement).RRNodeType) + ((node as unknown) as RRCanvasElement).rr_dataURL = value; } else if (tagName === 'img' && name === 'rr_dataURL') { const image = node as HTMLImageElement; if (!image.currentSrc.startsWith('data:')) { diff --git a/packages/rrweb/test/events/canvas-in-iframe.ts b/packages/rrweb/test/events/canvas-in-iframe.ts new file mode 100644 index 0000000000..972d8ec13c --- /dev/null +++ b/packages/rrweb/test/events/canvas-in-iframe.ts @@ -0,0 +1,181 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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, + }, + { + 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 }, + ], + id: 3, + }, + { type: 3, textContent: '\n ', id: 7 }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { type: 3, textContent: '\n ', id: 9 }, + { + type: 2, + tagName: 'iframe', + attributes: { id: 'target' }, + childNodes: [], + id: 19, + }, + { type: 3, textContent: '\n\n', id: 27 }, + ], + id: 8, + }, + ], + id: 2, + }, + ], + compatMode: 'BackCompat', + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: now + 200, + }, + // add an iframe + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + adds: [ + { + parentId: 19, + nextId: null, + node: { + type: 0, + childNodes: [ + { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + rootId: 30, + id: 32, + }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [], + rootId: 30, + id: 33, + }, + ], + rootId: 30, + id: 31, + }, + ], + compatMode: 'BackCompat', + id: 30, + }, + }, + ], + removes: [], + texts: [], + attributes: [], + isAttachIframe: true, + }, + timestamp: now + 500, + }, + // add two canvas, one is blank ans the other is filled with data + { + type: EventType.IncrementalSnapshot, + data: { + source: 0, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 33, + nextId: null, + node: { + type: 2, + tagName: 'canvas', + attributes: { + width: '10', + height: '10', + id: 'blank_canvas', + }, + childNodes: [], + rootId: 30, + id: 34, + }, + }, + { + parentId: 33, + nextId: null, + node: { + type: 2, + tagName: 'canvas', + attributes: { + width: '10', + height: '10', + rr_dataURL: + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAB5JREFUKFNjZCASMBKpjmEQKvzPwIDqrEHoRozgBQC/ZQELU4DiXAAAAABJRU5ErkJggg==', + id: 'canvas_with_data', + }, + childNodes: [], + rootId: 30, + id: 35, + }, + }, + ], + }, + timestamp: now + 500, + }, +]; + +export default events; diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 4cfb3b0a31..01927e4dd1 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -15,6 +15,7 @@ import inputEvents from './events/input'; import iframeEvents from './events/iframe'; import shadowDomEvents from './events/shadow-dom'; import StyleSheetTextMutation from './events/style-sheet-text-mutation'; +import canvasInIframe from './events/canvas-in-iframe'; interface ISuite { code: string; @@ -613,6 +614,31 @@ describe('replayer', function () { ).toEqual('shadow dom two'); }); + it('can fast-forward mutation events containing painted canvas in iframe', async () => { + await page.evaluate(` + events = ${JSON.stringify(canvasInIframe)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.pause(550); + `); + const replayerIframe = await page.$('iframe'); + const contentDocument = await replayerIframe!.contentFrame()!; + const iframe = await contentDocument!.$('iframe'); + expect(iframe).not.toBeNull(); + const docInIFrame = await iframe?.contentFrame(); + expect(docInIFrame).not.toBeNull(); + const canvasElements = await docInIFrame!.$$('canvas'); + // The first canvas is a blank one and the second is a painted one. + expect(canvasElements.length).toEqual(2); + + const dataUrls = await docInIFrame?.$$eval('canvas', (elements) => + elements.map((element) => (element as HTMLCanvasElement).toDataURL()), + ); + expect(dataUrls?.length).toEqual(2); + // The painted canvas's data should not be empty. + expect(dataUrls![1]).not.toEqual(dataUrls![0]); + }); + it('can stream events in live mode', async () => { const status = await page.evaluate(` const { Replayer } = rrweb; From cb24aaf6aabf8728ebbb499453916ad09ef13aa2 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 31 Jul 2022 11:45:57 +0800 Subject: [PATCH 038/201] add benchmark for replayer's fast-forward mode (#947) * add benchmark for replayer's fast-forward mode * add comments * change the title * build: make benchmark a seperate command --- packages/rrweb/package.json | 7 +- .../benchmark/replay-fast-forward.test.ts | 170 ++++++++++++++++++ 2 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 packages/rrweb/test/benchmark/replay-fast-forward.test.ts diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 98e4547838..9ed387c2be 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -5,8 +5,8 @@ "scripts": { "prepare": "npm run prepack", "prepack": "npm run bundle", - "test": "npm run bundle:browser && jest", - "test:headless": "npm run bundle:browser && PUPPETEER_HEADLESS=true jest", + "test": "npm run bundle:browser && jest --testPathIgnorePatterns test/benchmark", + "test:headless": "PUPPETEER_HEADLESS=true npm run test", "test:watch": "PUPPETEER_HEADLESS=true npm run test -- --watch", "repl": "npm run bundle:browser && node scripts/repl.js", "dev": "yarn bundle:browser --watch", @@ -15,7 +15,8 @@ "typings": "tsc -d --declarationDir typings", "check-types": "tsc -noEmit", "prepublish": "npm run typings && npm run bundle", - "lint": "yarn eslint src" + "lint": "yarn eslint src", + "benchmark": "jest test/benchmark" }, "repository": { "type": "git", diff --git a/packages/rrweb/test/benchmark/replay-fast-forward.test.ts b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts new file mode 100644 index 0000000000..4e17acdfdf --- /dev/null +++ b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts @@ -0,0 +1,170 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import type { eventWithTime, recordOptions } from '../../src/types'; +import { launchPuppeteer, ISuite } from '../utils'; + +const suites: Array<{ + title: string; + eval: string; + eventsString?: string; + times?: number; // defaults to 5 +}> = [ + { + title: 'append 70 x 70 x 70 elements', + eval: ` + () => { + return new Promise((resolve) => { + const branches = 70; + const depth = 3; + // append children for the current node + function expand(node, depth) { + if (depth == 0) return; + for (let b = 0; b < branches; b++) { + const child = document.createElement('div'); + node.appendChild(child); + expand(child, depth - 1); + } + } + const frag = document.createDocumentFragment(); + const node = document.createElement('div'); + expand(node, depth); + frag.appendChild(node); + document.body.appendChild(frag); + resolve(); + }); + }; + `, + times: 3, + }, + { + title: 'append 1000 elements and reverse their order', + eval: ` + () => { + return new Promise(async (resolve) => { + const branches = 1000; + function waitForTimeout(timeout) { + return new Promise((resolve) => setTimeout(() => resolve(), timeout)); + } + const frag = document.createDocumentFragment(); + const node = document.createElement('div'); + for (let b = 0; b < branches; b++) { + const child = document.createElement('div'); + node.appendChild(child); + child.textContent = b + 1; + } + frag.appendChild(node); + document.body.appendChild(frag); + const children = node.children; + await waitForTimeout(0); + // reverse the order of children + for (let i = children.length - 1; i >= 0; i--) + node.appendChild(node.children[i]); + resolve(); + }); + }; + `, + times: 3, + }, +]; + +function avg(v: number[]): number { + return v.reduce((prev, cur) => prev + cur, 0) / v.length; +} + +describe('benchmark: replayer fast-forward performance', () => { + jest.setTimeout(240000); + let code: ISuite['code']; + let page: ISuite['page']; + let browser: ISuite['browser']; + + beforeAll(async () => { + browser = await launchPuppeteer({ + headless: true, + args: ['--disable-dev-shm-usage'], + }); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.min.js'); + code = fs.readFileSync(bundlePath, 'utf8'); + + for (const suite of suites) + suite.eventsString = await generateEvents(suite.eval); + }, 600_000); + + afterAll(async () => { + await browser.close(); + }); + + for (const suite of suites) { + it( + suite.title, + async () => { + suite.times = suite.times ?? 5; + const durations: number[] = []; + for (let i = 0; i < suite.times; i++) { + page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(` + + + `); + const duration = (await page.evaluate(() => { + const replayer = new (window as any).rrweb.Replayer( + (window as any).events, + ); + const start = Date.now(); + replayer.play(replayer.getMetaData().totalTime + 100); + return Date.now() - start; + })) as number; + durations.push(duration); + await page.close(); + } + + console.table([ + { + title: suite.title, + times: suite.times, + duration: avg(durations), + durations: durations.join(', '), + }, + ]); + }, + 60_000, + ); + } + + /** + * Get the recorded events after the mutation function is executed. + */ + async function generateEvents(mutateNodesFn: string): Promise { + const page = await browser.newPage(); + + await page.goto('about:blank'); + await page.setContent(` + + `); + const eventsString = (await page.evaluate((mutateNodesFn) => { + return new Promise((resolve) => { + const events: eventWithTime[] = []; + const options: recordOptions = { + emit: (event) => { + events.push(event); + }, + }; + const record = (window as any).rrweb.record; + record(options); + eval(mutateNodesFn)().then(() => { + resolve(JSON.stringify(events)); + }); + }); + }, mutateNodesFn)) as string; + + await page.close(); + return eventsString; + } +}); From abc035fd00972c3ffa1a9cf379b46f53cb74d394 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Sun, 31 Jul 2022 11:48:29 +0800 Subject: [PATCH 039/201] chore(release): publish new version - rrdom-nodejs@0.1.4 - rrdom@0.1.4 - rrweb-player@1.0.0-alpha.1 - rrweb-snapshot@2.0.0-alpha.1 - rrweb@2.0.0-alpha.1 --- packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/package.json | 4 ++-- packages/rrweb-player/package.json | 4 ++-- packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/package.json | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index b4d9d0b556..fd00d34576 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "0.1.3", + "version": "0.1.4", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,7 +49,7 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^0.1.3", - "rrweb-snapshot": "^2.0.0-alpha.0" + "rrdom": "^0.1.4", + "rrweb-snapshot": "^2.0.0-alpha.1" } } diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 234b1c28fc..e59fa66139 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "0.1.3", + "version": "0.1.4", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", @@ -46,6 +46,6 @@ "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.0" + "rrweb-snapshot": "^2.0.0-alpha.1" } } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 1ef553b96a..755350a567 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-player", - "version": "1.0.0-alpha.0", + "version": "1.0.0-alpha.1", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", @@ -24,7 +24,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.0" + "rrweb": "^2.0.0-alpha.1" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index d736b8c126..fd512672dc 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.0", + "version": "2.0.0-alpha.1", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 9ed387c2be..2c19de3e10 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.0", + "version": "2.0.0-alpha.1", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -80,7 +80,7 @@ "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^0.1.3", - "rrweb-snapshot": "^2.0.0-alpha.0" + "rrdom": "^0.1.4", + "rrweb-snapshot": "^2.0.0-alpha.1" } } From b2d56898a8edca37d113c6a9c41c4260111cd0bf Mon Sep 17 00:00:00 2001 From: Jinxing Lin <172601673@qq.com> Date: Sat, 6 Aug 2022 16:59:42 +0800 Subject: [PATCH 040/201] rrweb: add selection observer (#936) * rrweb: add selection observer * Update packages/rrweb/src/record/observer.ts Co-authored-by: Yun Feng * Update packages/rrweb/src/record/observer.ts Co-authored-by: Yun Feng * Update packages/rrweb/src/replay/index.ts Co-authored-by: Yun Feng * remove: repeat updateSelection * Update packages/rrweb/src/record/observer.ts Co-authored-by: Yun Feng * remove: utils sample events Co-authored-by: Yun Feng --- packages/rrweb/src/record/index.ts | 11 ++ packages/rrweb/src/record/observer.ts | 53 +++++++ packages/rrweb/src/replay/index.ts | 29 ++++ packages/rrweb/src/types.ts | 21 +++ packages/rrweb/test/events/selection.ts | 179 ++++++++++++++++++++++++ packages/rrweb/test/record.test.ts | 42 ++++++ packages/rrweb/test/replayer.test.ts | 16 +++ 7 files changed, 351 insertions(+) create mode 100644 packages/rrweb/test/events/selection.ts diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index fffb4ce8b8..a7b513c2a0 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -430,6 +430,17 @@ function record( }, }), ), + selectionCb: (p) => { + wrappedEmit( + wrapEvent({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Selection, + ...p, + }, + }), + ); + }, blockClass, ignoreClass, maskTextClass, diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index f745a413b2..9350906cec 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -35,6 +35,8 @@ import { styleDeclarationCallback, IWindow, MutationBufferParam, + SelectionRange, + selectionCallback, } from '../types'; import MutationBuffer from './mutation'; @@ -752,6 +754,47 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { }; } +function initSelectionObserver(param: observerParam): listenerHandler { + const { doc, mirror, blockClass, selectionCb } = param; + let collapsed = true; + + const updateSelection = () => { + const selection = doc.getSelection(); + + if (!selection || (collapsed && selection?.isCollapsed)) return; + + collapsed = selection.isCollapsed || false; + + const ranges: SelectionRange[] = []; + const count = selection.rangeCount || 0; + + for (let i = 0; i < count; i++) { + const range = selection.getRangeAt(i); + + const { startContainer, startOffset, endContainer, endOffset } = range; + + const blocked = + isBlocked(startContainer, blockClass, true) || + isBlocked(endContainer, blockClass, true); + + if (blocked) continue; + + ranges.push({ + start: mirror.getId(startContainer), + startOffset, + end: mirror.getId(endContainer), + endOffset, + }); + } + + selectionCb({ ranges }); + }; + + updateSelection(); + + return on('selectionchange', updateSelection); +} + function mergeHooks(o: observerParam, hooks: hooksParam) { const { mutationCb, @@ -765,6 +808,7 @@ function mergeHooks(o: observerParam, hooks: hooksParam) { styleDeclarationCb, canvasMutationCb, fontCb, + selectionCb, } = o; o.mutationCb = (...p: Arguments) => { if (hooks.mutation) { @@ -832,6 +876,12 @@ function mergeHooks(o: observerParam, hooks: hooksParam) { } fontCb(...p); }; + o.selectionCb = (...p: Arguments) => { + if (hooks.selection) { + hooks.selection(...p); + } + selectionCb(...p); + }; } export function initObservers( @@ -863,6 +913,8 @@ export function initObservers( : () => { // }; + const selectionObserver = initSelectionObserver(o); + // plugins const pluginHandlers: listenerHandler[] = []; for (const plugin of o.plugins) { @@ -883,6 +935,7 @@ export function initObservers( styleSheetObserver(); styleDeclarationObserver(); fontObserver(); + selectionObserver(); pluginHandlers.forEach((h) => h()); }; } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 70efba2b9a..df76ca9eeb 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1320,6 +1320,35 @@ export class Replayer { } break; } + case IncrementalSource.Selection: { + const selectionSet = new Set(); + const ranges = d.ranges.map( + ({ start, startOffset, end, endOffset }) => { + const startContainer = this.mirror.getNode(start); + const endContainer = this.mirror.getNode(end); + + if (!startContainer || !endContainer) return; + + const result = new Range(); + + result.setStart(startContainer, startOffset); + result.setEnd(endContainer, endOffset); + const doc = startContainer.ownerDocument; + const selection = doc?.getSelection(); + selection && selectionSet.add(selection); + + return { + range: result, + selection, + }; + }, + ); + + selectionSet.forEach((s) => s.removeAllRanges()); + + ranges.forEach((r) => r && r.selection?.addRange(r.range)); + break; + } default: } } diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 2442fb2d35..79ae529651 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -91,6 +91,7 @@ export enum IncrementalSource { Log, Drag, StyleDeclaration, + Selection, } export type mutationData = { @@ -142,6 +143,10 @@ export type fontData = { source: IncrementalSource.Font; } & fontParam; +export type selectionData = { + source: IncrementalSource.Selection; +} & selectionParam; + export type incrementalData = | mutationData | mousemoveData @@ -153,6 +158,7 @@ export type incrementalData = | styleSheetRuleData | canvasMutationData | fontData + | selectionData | styleDeclarationData; export type event = @@ -261,6 +267,7 @@ export type observerParam = { viewportResizeCb: viewportResizeCallback; inputCb: inputCallback; mediaInteractionCb: mediaInteractionCallback; + selectionCb: selectionCallback; blockClass: blockClass; blockSelector: string | null; ignoreClass: string; @@ -331,6 +338,7 @@ export type hooksParam = { styleDeclaration?: styleDeclarationCallback; canvasMutation?: canvasMutationCallback; font?: fontCallback; + selection?: selectionCallback; }; // https://dom.spec.whatwg.org/#interface-mutationrecord @@ -619,6 +627,19 @@ export type DocumentDimension = { absoluteScale: number; }; +export type SelectionRange = { + start: number; + startOffset: number; + end: number; + endOffset: number; +}; + +export type selectionParam = { + ranges: Array; +}; + +export type selectionCallback = (p: selectionParam) => void; + export type DeprecatedMirror = { map: { [key: number]: INode; diff --git a/packages/rrweb/test/events/selection.ts b/packages/rrweb/test/events/selection.ts new file mode 100644 index 0000000000..152350b0d4 --- /dev/null +++ b/packages/rrweb/test/events/selection.ts @@ -0,0 +1,179 @@ +import { EventType, eventWithTime, IncrementalSource } from '../../src/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: 'about:blank', + width: 1920, + height: 1080, + }, + timestamp: now + 200, + }, + { + type: EventType.FullSnapshot, + data: { + node: { + type: 0, + childNodes: [ + { + type: 1, + name: 'html', + publicId: '', + systemId: '', + id: 2, + }, + { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [ + { + type: 3, + textContent: '\\\\n ', + id: 5, + }, + { + type: 2, + tagName: 'meta', + attributes: { + charset: 'UTF-8', + }, + childNodes: [], + id: 6, + }, + { + type: 3, + textContent: '\\\\n ', + id: 7, + }, + ], + id: 4, + }, + { + type: 3, + textContent: '\\\\n ', + id: 8, + }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { + type: 3, + textContent: '\\\\n Lorem, ipsum\\\\n ', + id: 10, + }, + { + type: 2, + tagName: 'span', + attributes: { + id: 'startNode', + }, + childNodes: [ + { + type: 3, + textContent: + '\\\\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores culpa\\\\n corporis voluptas odit nobis recusandae inventore, magni praesentium\\\\n maiores perferendis quaerat excepturi officia minus velit voluptate\\\\n placeat minima? Nesciunt, eum!\\\\n ', + id: 12, + }, + ], + id: 11, + }, + { + type: 3, + textContent: + '\\\\n dolor sit amet consectetur adipisicing elit. Ad repellendus quas hic\\\\n deleniti, delectus consequatur voluptas aliquam dolore voluptates repellat\\\\n perferendis aperiam saepe maxime officia rem corporis beatae, assumenda\\\\n doloribus.\\\\n ', + id: 13, + }, + { + type: 2, + tagName: 'span', + attributes: { + id: 'endNode', + }, + childNodes: [ + { + type: 3, + textContent: + '\\\\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae\\\\n explicabo omnis dolores magni, ea doloribus possimus debitis reiciendis\\\\n distinctio perferendis nihil ipsum officiis pariatur laboriosam quas,\\\\n corrupti vero vitae minus.\\\\n ', + id: 15, + }, + ], + id: 14, + }, + { + type: 3, + textContent: '\\\\n \\\\n ', + id: 16, + }, + { + type: 2, + tagName: 'script', + attributes: {}, + childNodes: [ + { + type: 3, + textContent: 'SCRIPT_PLACEHOLDER', + id: 18, + }, + ], + id: 17, + }, + { + type: 3, + textContent: '\\\\n \\\\n \\\\n\\\\n', + id: 19, + }, + ], + id: 9, + }, + ], + id: 3, + }, + ], + id: 1, + }, + initialOffset: { + left: 0, + top: 0, + }, + }, + timestamp: now + 300, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Selection, + ranges: [ + { + start: 12, + startOffset: 11, + end: 15, + endOffset: 6, + }, + ], + }, + timestamp: now + 400, + }, +]; + +export default events; diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 7fd98d92f5..f9b46aea02 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -8,6 +8,7 @@ import { EventType, IncrementalSource, styleSheetRuleData, + selectionData, } from '../src/types'; import { assertSnapshot, launchPuppeteer, waitForRAF } from './utils'; @@ -211,6 +212,47 @@ describe('record', function (this: ISuite) { assertSnapshot(ctx.events); }); + it('should record selection event', async () => { + await ctx.page.evaluate(() => { + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + emit: ((window as unknown) as IWindow).emit, + }); + const startNode = document.createElement('p'); + + startNode.innerText = + 'Lorem ipsum dolor sit amet consectetur adipisicing elit.'; + + const endNode = document.createElement('span'); + endNode.innerText = + 'nihil ipsum officiis pariatur laboriosam quas,corrupti vero vitae minus.'; + + document.body.appendChild(startNode); + document.body.appendChild(endNode); + + const selection = window.getSelection(); + const range = new Range(); + + range.setStart(startNode!.firstChild!, 10); + range.setEnd(endNode!.firstChild!, 2); + + selection?.addRange(range); + }); + await waitForRAF(ctx.page); + const selectionData = ctx.events + .filter(({ type, data }) => { + return ( + type === EventType.IncrementalSnapshot && + data.source === IncrementalSource.Selection + ); + }) + .map((ev) => ev.data as selectionData); + + expect(selectionData.length).toEqual(1); + expect(selectionData[0].ranges[0].startOffset).toEqual(10); + expect(selectionData[0].ranges[0].endOffset).toEqual(2); + }); + it('can add custom event', async () => { await ctx.page.evaluate(() => { const { record, addCustomEvent } = ((window as unknown) as IWindow).rrweb; diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 01927e4dd1..c0c8a07827 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -13,6 +13,7 @@ import orderingEvents from './events/ordering'; import scrollEvents from './events/scroll'; import inputEvents from './events/input'; import iframeEvents from './events/iframe'; +import selectionEvents from './events/selection'; import shadowDomEvents from './events/shadow-dom'; import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; @@ -202,6 +203,21 @@ describe('replayer', function () { ); }); + it('can restore selection', async () => { + await page.evaluate(`events = ${JSON.stringify(selectionEvents)}`); + const [startOffset, endOffset] = (await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(1500); + const range = replayer.iframe.contentDocument.getSelection().getRangeAt(0); + + [range.startOffset, range.endOffset]; + `)) as [startOffset: number, endOffset: number]; + + expect(startOffset).toEqual(11); + expect(endOffset).toEqual(6); + }); + it('can fast forward past StyleSheetRule deletion on virtual elements', async () => { await page.evaluate(`events = ${JSON.stringify(styleSheetRuleEvents)}`); From fd85c79ea9922e91dcc9aabeeba69053abccfa79 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sat, 6 Aug 2022 11:00:05 +0200 Subject: [PATCH 041/201] Handle negative ids in rrdom correctly + extra tests (#927) * inline stylesheets when loaded * set empty link elements to loaded by default * Clean up stylesheet manager * Remove attribute mutation code * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/scripts/repl.js * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/src/record/index.ts * Add todo * Move require out of time sensitive assert * Add waitForRAF, its more reliable than waitForTimeout * Remove flaky tests * Add recording stylesheets in iframes * Remove variability from flaky test * Make test more robust * Fix naming * Add test cases for inlineImages * Add test cases for inlineImages * Record iframe mutations cross page * Test: should record images inside iframe with blob url after iframe was reloaded * Handle negative ids in rrdom correctly When iframes get inserted they create untracked elements, both on the dom and rrdom side. Because they are untracked they generate negative numbers when fetching the id from mirror. This creates a problem when comparing and fetching ids across mirrors. This commit tries to get away from using negative ids as much as possible in rrdom's comparisons * Update packages/rrdom/src/diff.ts Co-authored-by: Yun Feng * Start unserialized nodes at -2 This way we don't accidentally think of them as mirror misses * Set unserialized id starting number at -2 * Remove duplication Co-authored-by: Yun Feng --- packages/rrdom/src/diff.ts | 33 +- packages/rrdom/src/index.ts | 5 +- .../__snapshots__/virtual-dom.test.ts.snap | 260 +- packages/rrdom/test/diff.test.ts | 106 + packages/rrdom/test/virtual-dom.test.ts | 38 +- packages/rrweb-snapshot/src/snapshot.ts | 3 +- .../test/html/picture-blob-in-frame.html | 5 + .../test/html/picture-blob.html | 16 + .../test/html/picture-in-frame.html | 5 + .../rrweb-snapshot/test/integration.test.ts | 69 + packages/rrweb-snapshot/test/utils.ts | 11 + packages/rrweb/src/record/index.ts | 2 + packages/rrweb/src/record/mutation.ts | 17 +- packages/rrweb/src/types.ts | 2 + .../__snapshots__/integration.test.ts.snap | 2423 +++++++++++++---- .../test/__snapshots__/replayer.test.ts.snap | 71 +- packages/rrweb/test/html/assets/robot.png | Bin 0 -> 11004 bytes .../rrweb/test/html/frame-image-blob-url.html | 11 + packages/rrweb/test/html/image-blob-url.html | 21 + packages/rrweb/test/integration.test.ts | 79 + packages/rrweb/test/replayer.test.ts | 29 +- packages/rrweb/test/utils.ts | 63 +- 22 files changed, 2570 insertions(+), 699 deletions(-) create mode 100644 packages/rrweb-snapshot/test/html/picture-blob-in-frame.html create mode 100644 packages/rrweb-snapshot/test/html/picture-blob.html create mode 100644 packages/rrweb-snapshot/test/html/picture-in-frame.html create mode 100644 packages/rrweb-snapshot/test/utils.ts create mode 100644 packages/rrweb/test/html/assets/robot.png create mode 100644 packages/rrweb/test/html/frame-image-blob-url.html create mode 100644 packages/rrweb/test/html/image-blob-url.html diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index f0896088a9..8456dd3398 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -272,37 +272,57 @@ function diffChildren( let oldIdToIndex: Record | undefined = undefined, indexInOld; while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) { + const oldStartId = replayer.mirror.getId(oldStartNode); + const oldEndId = replayer.mirror.getId(oldEndNode); + const newStartId = rrnodeMirror.getId(newStartNode); + const newEndId = rrnodeMirror.getId(newEndNode); + + // rrdom contains elements with negative ids, we don't want to accidentally match those to a mirror mismatch (-1) id. + // Negative oldStartId happen when nodes are not in the mirror, but are in the DOM. + // eg.iframes come with a document, html, head and body nodes. + // thats why below we always check if an id is negative. + if (oldStartNode === undefined) { oldStartNode = oldChildren[++oldStartIndex]; } else if (oldEndNode === undefined) { oldEndNode = oldChildren[--oldEndIndex]; } else if ( - replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newStartNode) + oldStartId !== -1 && + // same first element? + oldStartId === newStartId ) { diff(oldStartNode, newStartNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newStartNode = newChildren[++newStartIndex]; } else if ( - replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newEndNode) + oldEndId !== -1 && + // same last element? + oldEndId === newEndId ) { diff(oldEndNode, newEndNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( - replayer.mirror.getId(oldStartNode) === rrnodeMirror.getId(newEndNode) + oldStartId !== -1 && + // is the first old element the same as the last new element? + oldStartId === newEndId ) { parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling); diff(oldStartNode, newEndNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( - replayer.mirror.getId(oldEndNode) === rrnodeMirror.getId(newStartNode) + oldEndId !== -1 && + // is the last old element the same as the first new element? + oldEndId === newStartId ) { parentNode.insertBefore(oldEndNode, oldStartNode); diff(oldEndNode, newStartNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newStartNode = newChildren[++newStartIndex]; } else { + // none of the elements matched + if (!oldIdToIndex) { oldIdToIndex = {}; for (let i = oldStartIndex; i <= oldEndIndex; i++) { @@ -378,8 +398,11 @@ export function createOrGetNode( domMirror: NodeMirror, rrnodeMirror: Mirror, ): Node { - let node = domMirror.getNode(rrnodeMirror.getId(rrNode)); + const nodeId = rrnodeMirror.getId(rrNode); const sn = rrnodeMirror.getMeta(rrNode); + let node: Node | null = null; + // negative ids shouldn't be compared accross mirrors + if (nodeId > -1) node = domMirror.getNode(nodeId); if (node !== null) return node; switch (rrNode.RRNodeType) { case RRNodeType.Document: diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index 16da3ed8b9..0caefa8d92 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -33,10 +33,11 @@ import { } from './document'; export class RRDocument extends BaseRRDocumentImpl(RRNode) { + private UNSERIALIZED_STARTING_ID = -2; // In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules. // These unserialized nodes may interfere the execution of the diff algorithm. // The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes. - private _unserializedId = -1; + private _unserializedId = this.UNSERIALIZED_STARTING_ID; /** * Every time the id is used, it will minus 1 automatically to avoid collisions. @@ -135,7 +136,7 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) { open() { super.open(); - this._unserializedId = -1; + this._unserializedId = this.UNSERIALIZED_STARTING_ID; } } diff --git a/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap b/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap index c1dc00248b..2286457a28 100644 --- a/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap +++ b/packages/rrdom/test/__snapshots__/virtual-dom.test.ts.snap @@ -1,118 +1,118 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`RRDocument for browser environment create a RRDocument from a html document can build from a common html 1`] = ` -"-1 RRDocument - -2 RRDocumentType - -3 HTML lang=\\"en\\" - -4 HEAD - -5 RRText text=\\"\\\\n \\" - -6 META charset=\\"UTF-8\\" - -7 RRText text=\\"\\\\n \\" - -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" - -9 RRText text=\\"\\\\n \\" - -10 TITLE - -11 RRText text=\\"Main\\" - -12 RRText text=\\"\\\\n \\" - -13 LINK rel=\\"stylesheet\\" href=\\"somelink\\" - -14 RRText text=\\"\\\\n \\" - -15 STYLE - -16 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url('main.css');\\\\n \\" - -17 RRText text=\\"\\\\n \\" - -18 RRText text=\\"\\\\n \\" - -19 BODY - -20 RRText text=\\"\\\\n \\" - -21 H1 - -22 RRText text=\\"This is a h1 heading\\" - -23 RRText text=\\"\\\\n \\" - -24 H1 style=\\"font-size: 16px\\" - -25 RRText text=\\"This is a h1 heading with styles\\" - -26 RRText text=\\"\\\\n \\" - -27 DIV id=\\"block1\\" class=\\"blocks blocks1\\" - -28 RRText text=\\"\\\\n \\" - -29 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\" - -30 RRText text=\\"\\\\n Text 1\\\\n \\" - -31 DIV id=\\"block3\\" - -32 RRText text=\\"\\\\n \\" - -33 P - -34 RRText text=\\"This is a paragraph\\" - -35 RRText text=\\"\\\\n \\" - -36 BUTTON - -37 RRText text=\\"button1\\" - -38 RRText text=\\"\\\\n \\" - -39 RRText text=\\"\\\\n Text 2\\\\n \\" - -40 RRText text=\\"\\\\n \\" - -41 IMG src=\\"somelink\\" alt=\\"This is an image\\" - -42 RRText text=\\"\\\\n \\" - -43 RRComment text=\\" This is a line of comment \\" - -44 RRText text=\\"\\\\n \\" - -45 FORM - -46 RRText text=\\"\\\\n \\" - -47 INPUT type=\\"text\\" id=\\"input1\\" - -48 RRText text=\\"\\\\n \\" - -49 RRText text=\\"\\\\n \\" - -50 RRText text=\\"\\\\n \\\\n\\\\n\\" +"-2 RRDocument + -3 RRDocumentType + -4 HTML lang=\\"en\\" + -5 HEAD + -6 RRText text=\\"\\\\n \\" + -7 META charset=\\"UTF-8\\" + -8 RRText text=\\"\\\\n \\" + -9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -10 RRText text=\\"\\\\n \\" + -11 TITLE + -12 RRText text=\\"Main\\" + -13 RRText text=\\"\\\\n \\" + -14 LINK rel=\\"stylesheet\\" href=\\"somelink\\" + -15 RRText text=\\"\\\\n \\" + -16 STYLE + -17 RRText text=\\"\\\\n h1 {\\\\n color: 'black';\\\\n }\\\\n .blocks {\\\\n padding: 0;\\\\n }\\\\n .blocks1 {\\\\n margin: 0;\\\\n }\\\\n #block1 {\\\\n width: 100px;\\\\n height: 200px;\\\\n }\\\\n @import url('main.css');\\\\n \\" + -18 RRText text=\\"\\\\n \\" + -19 RRText text=\\"\\\\n \\" + -20 BODY + -21 RRText text=\\"\\\\n \\" + -22 H1 + -23 RRText text=\\"This is a h1 heading\\" + -24 RRText text=\\"\\\\n \\" + -25 H1 style=\\"font-size: 16px\\" + -26 RRText text=\\"This is a h1 heading with styles\\" + -27 RRText text=\\"\\\\n \\" + -28 DIV id=\\"block1\\" class=\\"blocks blocks1\\" + -29 RRText text=\\"\\\\n \\" + -30 DIV id=\\"block2\\" class=\\"blocks blocks1 :hover\\" + -31 RRText text=\\"\\\\n Text 1\\\\n \\" + -32 DIV id=\\"block3\\" + -33 RRText text=\\"\\\\n \\" + -34 P + -35 RRText text=\\"This is a paragraph\\" + -36 RRText text=\\"\\\\n \\" + -37 BUTTON + -38 RRText text=\\"button1\\" + -39 RRText text=\\"\\\\n \\" + -40 RRText text=\\"\\\\n Text 2\\\\n \\" + -41 RRText text=\\"\\\\n \\" + -42 IMG src=\\"somelink\\" alt=\\"This is an image\\" + -43 RRText text=\\"\\\\n \\" + -44 RRComment text=\\" This is a line of comment \\" + -45 RRText text=\\"\\\\n \\" + -46 FORM + -47 RRText text=\\"\\\\n \\" + -48 INPUT type=\\"text\\" id=\\"input1\\" + -49 RRText text=\\"\\\\n \\" + -50 RRText text=\\"\\\\n \\" + -51 RRText text=\\"\\\\n \\\\n\\\\n\\" " `; exports[`RRDocument for browser environment create a RRDocument from a html document can build from a html containing nested shadow doms 1`] = ` -"-1 RRDocument - -2 RRDocumentType - -3 HTML lang=\\"en\\" - -4 HEAD - -5 RRText text=\\"\\\\n \\" - -6 META charset=\\"UTF-8\\" - -7 RRText text=\\"\\\\n \\" - -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" - -9 RRText text=\\"\\\\n \\" - -10 TITLE - -11 RRText text=\\"shadow dom\\" - -12 RRText text=\\"\\\\n \\" - -13 RRText text=\\"\\\\n \\" - -14 BODY - -15 RRText text=\\"\\\\n \\" - -16 DIV - -17 SHADOWROOT - -18 RRText text=\\"\\\\n \\" - -19 SPAN - -20 RRText text=\\" shadow dom one \\" - -21 RRText text=\\"\\\\n \\" - -22 DIV - -23 SHADOWROOT - -24 RRText text=\\"\\\\n \\" - -25 SPAN - -26 RRText text=\\" shadow dom two \\" - -27 RRText text=\\"\\\\n \\" - -28 RRText text=\\"\\\\n \\\\n \\" - -29 RRText text=\\"\\\\n \\" - -30 RRText text=\\"\\\\n \\\\n \\" - -31 RRText text=\\"\\\\n \\\\n\\\\n\\" +"-2 RRDocument + -3 RRDocumentType + -4 HTML lang=\\"en\\" + -5 HEAD + -6 RRText text=\\"\\\\n \\" + -7 META charset=\\"UTF-8\\" + -8 RRText text=\\"\\\\n \\" + -9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -10 RRText text=\\"\\\\n \\" + -11 TITLE + -12 RRText text=\\"shadow dom\\" + -13 RRText text=\\"\\\\n \\" + -14 RRText text=\\"\\\\n \\" + -15 BODY + -16 RRText text=\\"\\\\n \\" + -17 DIV + -18 SHADOWROOT + -19 RRText text=\\"\\\\n \\" + -20 SPAN + -21 RRText text=\\" shadow dom one \\" + -22 RRText text=\\"\\\\n \\" + -23 DIV + -24 SHADOWROOT + -25 RRText text=\\"\\\\n \\" + -26 SPAN + -27 RRText text=\\" shadow dom two \\" + -28 RRText text=\\"\\\\n \\" + -29 RRText text=\\"\\\\n \\\\n \\" + -30 RRText text=\\"\\\\n \\" + -31 RRText text=\\"\\\\n \\\\n \\" + -32 RRText text=\\"\\\\n \\\\n\\\\n\\" " `; exports[`RRDocument for browser environment create a RRDocument from a html document can build from a xml page 1`] = ` -"-1 RRDocument - -2 XML - -3 RRCDATASection data=\\"Some data & then some\\" +"-2 RRDocument + -3 XML + -4 RRCDATASection data=\\"Some data & then some\\" " `; exports[`RRDocument for browser environment create a RRDocument from a html document can build from an iframe html 1`] = ` -"-1 RRDocument - -2 RRDocumentType - -3 HTML lang=\\"en\\" - -4 HEAD - -5 RRText text=\\"\\\\n \\" - -6 META charset=\\"UTF-8\\" - -7 RRText text=\\"\\\\n \\" - -8 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" - -9 RRText text=\\"\\\\n \\" - -10 TITLE - -11 RRText text=\\"Iframe\\" - -12 RRText text=\\"\\\\n \\" - -13 RRText text=\\"\\\\n \\" - -14 BODY - -15 RRText text=\\"\\\\n \\" - -16 IFRAME id=\\"iframe1\\" srcdoc=\\" +"-2 RRDocument + -3 RRDocumentType + -4 HTML lang=\\"en\\" + -5 HEAD + -6 RRText text=\\"\\\\n \\" + -7 META charset=\\"UTF-8\\" + -8 RRText text=\\"\\\\n \\" + -9 META name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" + -10 RRText text=\\"\\\\n \\" + -11 TITLE + -12 RRText text=\\"Iframe\\" + -13 RRText text=\\"\\\\n \\" + -14 RRText text=\\"\\\\n \\" + -15 BODY + -16 RRText text=\\"\\\\n \\" + -17 IFRAME id=\\"iframe1\\" srcdoc=\\" @@ -126,35 +126,35 @@ exports[`RRDocument for browser environment create a RRDocument from a html docu '); + + const iframe = document.querySelector('iframe')!; + // Remove everthing from the iframe but the root html element + // `buildNodeWithSn` injects docType elements to trigger compatMode in iframes + iframe.contentDocument!.write( + '', + ); + + replayer.mirror.add(iframe.contentDocument!, { + id: 1, + type: 0, + childNodes: [ + { + id: 2, + rootId: 1, + type: 2, + tagName: 'html', + childNodes: [], + attributes: {}, + }, + ], + } as serializedNodeWithId); + replayer.mirror.add(iframe.contentDocument!.childNodes[0], { + id: 2, + rootId: 1, + type: 2, + tagName: 'html', + childNodes: [], + attributes: {}, + } as serializedNodeWithId); + + const rrDocument = new RRDocument(); + rrDocument.mirror.add(rrDocument, getDefaultSN(rrDocument, 1)); + const docType = rrDocument.createDocumentType('html', '', ''); + rrDocument.mirror.add(docType, getDefaultSN(docType, 2)); + rrDocument.appendChild(docType); + const htmlEl = rrDocument.createElement('html'); + rrDocument.mirror.add(htmlEl, getDefaultSN(htmlEl, 3)); + rrDocument.appendChild(htmlEl); + const styleEl = rrDocument.createElement('style'); + rrDocument.mirror.add(styleEl, getDefaultSN(styleEl, 4)); + htmlEl.appendChild(styleEl); + const headEl = rrDocument.createElement('head'); + rrDocument.mirror.add(headEl, getDefaultSN(headEl, 5)); + htmlEl.appendChild(headEl); + const bodyEl = rrDocument.createElement('body'); + rrDocument.mirror.add(bodyEl, getDefaultSN(bodyEl, 6)); + htmlEl.appendChild(bodyEl); + + diff(iframe.contentDocument!, rrDocument, replayer); + expect(iframe.contentDocument!.childNodes.length).toBe(2); + const element = iframe.contentDocument!.childNodes[0] as HTMLElement; + expect(element.nodeType).toBe(element.DOCUMENT_TYPE_NODE); + expect(mirror.getId(element)).toEqual(2); + }); + + it('should remove children from document before adding new nodes 3', () => { + document.write(''); + + const iframeInDom = document.querySelector('iframe')!; + + replayer.mirror.add(iframeInDom, { + id: 3, + type: 2, + rootId: 1, + tagName: 'iframe', + childNodes: [], + attributes: {}, + } as serializedNodeWithId); + replayer.mirror.add(iframeInDom.contentDocument!, { + id: 4, + type: 0, + childNodes: [], + } as serializedNodeWithId); + + const rrDocument = new RRDocument(); + + const rrIframeEl = rrDocument.createElement('iframe'); + rrDocument.mirror.add(rrIframeEl, getDefaultSN(rrIframeEl, 3)); + rrDocument.appendChild(rrIframeEl); + rrDocument.mirror.add( + rrIframeEl.contentDocument!, + getDefaultSN(rrIframeEl.contentDocument!, 4), + ); + + const rrDocType = rrDocument.createDocumentType('html', '', ''); + rrIframeEl.contentDocument.appendChild(rrDocType); + const rrHtmlEl = rrDocument.createElement('html'); + rrDocument.mirror.add(rrHtmlEl, getDefaultSN(rrHtmlEl, 6)); + rrIframeEl.contentDocument.appendChild(rrHtmlEl); + const rrHeadEl = rrDocument.createElement('head'); + rrDocument.mirror.add(rrHeadEl, getDefaultSN(rrHeadEl, 8)); + rrHtmlEl.appendChild(rrHeadEl); + const bodyEl = rrDocument.createElement('body'); + rrDocument.mirror.add(bodyEl, getDefaultSN(bodyEl, 9)); + rrHtmlEl.appendChild(bodyEl); + + diff(iframeInDom, rrIframeEl, replayer); + expect(iframeInDom.contentDocument!.childNodes.length).toBe(2); + const element = iframeInDom.contentDocument!.childNodes[0] as HTMLElement; + expect(element.nodeType).toBe(element.DOCUMENT_TYPE_NODE); + expect(mirror.getId(element)).toEqual(-1); + }); }); describe('create or get a Node', () => { diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index b99a34325e..57aed3e72f 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -78,24 +78,24 @@ describe('RRDocument for browser environment', () => { const rrdom = new RRDocument(); let rrNode = buildFromNode(document, rrdom, mirror)!; expect(mirror.getMeta(document)).toBeDefined(); - expect(mirror.getId(document)).toEqual(-1); + expect(mirror.getId(document)).toEqual(-2); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document); - expect(rrdom.mirror.getId(rrNode)).toEqual(-1); + expect(rrdom.mirror.getId(rrNode)).toEqual(-2); expect(rrNode).toBe(rrdom); // build from document type expect(mirror.getMeta(document.doctype!)).toBeNull(); rrNode = buildFromNode(document.doctype!, rrdom, mirror)!; expect(mirror.getMeta(document.doctype!)).toBeDefined(); - expect(mirror.getId(document.doctype)).toEqual(-2); + expect(mirror.getId(document.doctype)).toEqual(-3); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual( RRNodeType.DocumentType, ); - expect(rrdom.mirror.getId(rrNode)).toEqual(-2); + expect(rrdom.mirror.getId(rrNode)).toEqual(-3); // build from element expect(mirror.getMeta(document.documentElement)).toBeNull(); @@ -105,33 +105,33 @@ describe('RRDocument for browser environment', () => { mirror, )!; expect(mirror.getMeta(document.documentElement)).toBeDefined(); - expect(mirror.getId(document.documentElement)).toEqual(-3); + expect(mirror.getId(document.documentElement)).toEqual(-4); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Element); - expect(rrdom.mirror.getId(rrNode)).toEqual(-3); + expect(rrdom.mirror.getId(rrNode)).toEqual(-4); // build from text const text = document.createTextNode('text'); expect(mirror.getMeta(text)).toBeNull(); rrNode = buildFromNode(text, rrdom, mirror)!; expect(mirror.getMeta(text)).toBeDefined(); - expect(mirror.getId(text)).toEqual(-4); + expect(mirror.getId(text)).toEqual(-5); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Text); - expect(rrdom.mirror.getId(rrNode)).toEqual(-4); + expect(rrdom.mirror.getId(rrNode)).toEqual(-5); // build from comment const comment = document.createComment('comment'); expect(mirror.getMeta(comment)).toBeNull(); rrNode = buildFromNode(comment, rrdom, mirror)!; expect(mirror.getMeta(comment)).toBeDefined(); - expect(mirror.getId(comment)).toEqual(-5); + expect(mirror.getId(comment)).toEqual(-6); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Comment); - expect(rrdom.mirror.getId(rrNode)).toEqual(-5); + expect(rrdom.mirror.getId(rrNode)).toEqual(-6); // build from CDATASection const xmlDoc = new DOMParser().parseFromString( @@ -144,11 +144,11 @@ describe('RRDocument for browser environment', () => { expect(mirror.getMeta(cdataSection)).toBeNull(); rrNode = buildFromNode(cdataSection, rrdom, mirror)!; expect(mirror.getMeta(cdataSection)).toBeDefined(); - expect(mirror.getId(cdataSection)).toEqual(-6); + expect(mirror.getId(cdataSection)).toEqual(-7); expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.CDATA); - expect(rrdom.mirror.getId(rrNode)).toEqual(-6); + expect(rrdom.mirror.getId(rrNode)).toEqual(-7); expect(rrNode.textContent).toEqual(cdata); }); @@ -184,8 +184,8 @@ describe('RRDocument for browser environment', () => { expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document); - expect(rrdom.mirror.getId(rrNode)).toEqual(-1); - expect(mirror.getId(iframe.contentDocument)).toEqual(-1); + expect(rrdom.mirror.getId(rrNode)).toEqual(-2); + expect(mirror.getId(iframe.contentDocument)).toEqual(-2); expect(rrNode).toBe(RRIFrame.contentDocument); }); @@ -203,8 +203,8 @@ describe('RRDocument for browser environment', () => { )!; expect(rrNode).not.toBeNull(); expect(rrdom.mirror.getMeta(rrNode)).toBeDefined(); - expect(rrdom.mirror.getId(rrNode)).toEqual(-1); - expect(mirror.getId(div.shadowRoot)).toEqual(-1); + expect(rrdom.mirror.getId(rrNode)).toEqual(-2); + expect(mirror.getId(div.shadowRoot)).toEqual(-2); expect(rrNode.RRNodeType).toEqual(RRNodeType.Element); expect((rrNode as RRElement).tagName).toEqual('SHADOWROOT'); expect(rrNode).toBe(parentRRNode.shadowRoot); @@ -296,7 +296,7 @@ describe('RRDocument for browser environment', () => { describe('RRDocument build for virtual dom', () => { it('can access a unique, decremented unserializedId every time', () => { const node = new RRDocument(); - for (let i = 1; i <= 100; i++) expect(node.unserializedId).toBe(-i); + for (let i = 2; i <= 100; i++) expect(node.unserializedId).toBe(-i); }); it('can create a new RRDocument', () => { @@ -357,12 +357,12 @@ describe('RRDocument for browser environment', () => { const documentType = dom.createDocumentType('html', '', ''); dom.appendChild(documentType); expect(dom.childNodes[0]).toBe(documentType); - expect(dom.unserializedId).toBe(-1); expect(dom.unserializedId).toBe(-2); + expect(dom.unserializedId).toBe(-3); expect(dom.close()); expect(dom.open()); expect(dom.childNodes.length).toEqual(0); - expect(dom.unserializedId).toBe(-1); + expect(dom.unserializedId).toBe(-2); }); it('can execute a dummy getContext function in RRCanvasElement', () => { diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index a61f650a65..38df3b6954 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -388,7 +388,8 @@ function onceIframeLoaded( // iframe was already loaded, make sure we wait to trigger the listener // till _after_ the mutation that found this iframe has had time to process setTimeout(listener, 0); - return; + + return iframeEl.addEventListener('load', listener); // keep listing for future loads } // use default listener iframeEl.addEventListener('load', listener); diff --git a/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html b/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html new file mode 100644 index 0000000000..f6237f22b2 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-blob-in-frame.html @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/rrweb-snapshot/test/html/picture-blob.html b/packages/rrweb-snapshot/test/html/picture-blob.html new file mode 100644 index 0000000000..d2a32658a1 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-blob.html @@ -0,0 +1,16 @@ + + + This is a robot + + + diff --git a/packages/rrweb-snapshot/test/html/picture-in-frame.html b/packages/rrweb-snapshot/test/html/picture-in-frame.html new file mode 100644 index 0000000000..31684d2cda --- /dev/null +++ b/packages/rrweb-snapshot/test/html/picture-in-frame.html @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index cba669c152..6ee32f9426 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -6,6 +6,7 @@ import * as puppeteer from 'puppeteer'; import * as rollup from 'rollup'; import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; +import { waitForRAF } from './utils'; const _typescript = (typescript as unknown) as () => rollup.Plugin; @@ -207,6 +208,74 @@ iframe.contentDocument.querySelector('center').clientHeight assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); + + it('correctly saves blob:images offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-blob.html', { + waitUntil: 'load', + }); + await page.waitForSelector('img', { timeout: 1000 }); + await page.evaluate(`${code}var snapshot = rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false + })`); + await page.waitFor(100); + const snapshot = await page.evaluate('JSON.stringify(snapshot, null, 2);'); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); + + it('correctly saves images in iframes offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-in-frame.html', { + waitUntil: 'load', + }); + await page.waitForSelector('iframe', { timeout: 1000 }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false, + onIframeLoad: function(iframe, sn) { + window.snapshot = sn; + } + })`); + await page.waitFor(100); + const snapshot = await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + ); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); + + it('correctly saves blob:images in iframes offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('http://localhost:3030/html/picture-blob-in-frame.html', { + waitUntil: 'load', + }); + await page.waitForSelector('iframe', { timeout: 1000 }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false, + onIframeLoad: function(iframe, sn) { + window.snapshot = sn; + } + })`); + await page.waitFor(100); + const snapshot = await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + ); + assert(snapshot.includes('"rr_dataURL"')); + assert(snapshot.includes('data:image/webp;base64,')); + }); }); describe('iframe integration tests', function (this: ISuite) { diff --git a/packages/rrweb-snapshot/test/utils.ts b/packages/rrweb-snapshot/test/utils.ts new file mode 100644 index 0000000000..43d4484bb4 --- /dev/null +++ b/packages/rrweb-snapshot/test/utils.ts @@ -0,0 +1,11 @@ +import * as puppeteer from 'puppeteer'; + +export async function waitForRAF(page: puppeteer.Page) { + return await page.evaluate(() => { + return new Promise((resolve) => { + requestAnimationFrame(() => { + requestAnimationFrame(resolve); + }); + }); + }); +} diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index a7b513c2a0..756c21c56b 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -249,6 +249,7 @@ function record( iframeManager, stylesheetManager, canvasManager, + keepIframeSrcFn, }, mirror, }); @@ -455,6 +456,7 @@ function record( doc, maskInputFn, maskTextFn, + keepIframeSrcFn, blockSelector, slimDOMOptions, mirror, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 425445cd11..df3b3017c2 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -165,6 +165,7 @@ export default class MutationBuffer { private maskInputOptions: observerParam['maskInputOptions']; private maskTextFn: observerParam['maskTextFn']; private maskInputFn: observerParam['maskInputFn']; + private keepIframeSrcFn: observerParam['keepIframeSrcFn']; private recordCanvas: observerParam['recordCanvas']; private inlineImages: observerParam['inlineImages']; private slimDOMOptions: observerParam['slimDOMOptions']; @@ -186,6 +187,7 @@ export default class MutationBuffer { 'maskInputOptions', 'maskTextFn', 'maskInputFn', + 'keepIframeSrcFn', 'recordCanvas', 'inlineImages', 'slimDOMOptions', @@ -485,6 +487,19 @@ export default class MutationBuffer { let item: attributeCursor | undefined = this.attributes.find( (a) => a.node === m.target, ); + if ( + target.tagName === 'IFRAME' && + m.attributeName === 'src' && + !this.keepIframeSrcFn(value as string) + ) { + if (!(target as HTMLIFrameElement).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 + m.attributeName = 'rr_src'; + } else { + return; + } + } if (!item) { item = { node: m.target, @@ -528,7 +543,7 @@ export default class MutationBuffer { // overwrite attribute if the mutations was triggered in same time item.attributes[m.attributeName!] = transformAttribute( this.doc, - (m.target as HTMLElement).tagName, + target.tagName, m.attributeName!, value!, ); diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 79ae529651..d9a14817cf 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -276,6 +276,7 @@ export type observerParam = { maskInputOptions: MaskInputOptions; maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; + keepIframeSrcFn: KeepIframeSrcFn; inlineStylesheet: boolean; styleSheetRuleCb: styleSheetRuleCallback; styleDeclarationCb: styleDeclarationCallback; @@ -315,6 +316,7 @@ export type MutationBufferParam = Pick< | 'maskInputOptions' | 'maskTextFn' | 'maskInputFn' + | 'keepIframeSrcFn' | 'recordCanvas' | 'inlineImages' | 'slimDOMOptions' diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 53b8206015..b25764af26 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -9096,7 +9096,7 @@ exports[`record integration tests should record dynamic CSS changes 1`] = ` ]" `; -exports[`record integration tests should record input userTriggered values if userTriggeredOnInput is enabled 1`] = ` +exports[`record integration tests should record images inside iframe with blob url 1`] = ` "[ { \\"type\\": 0, @@ -9173,21 +9173,6 @@ exports[`record integration tests should record input userTriggered values if us \\"textContent\\": \\"\\\\n \\", \\"id\\": 9 }, - { - \\"type\\": 2, - \\"tagName\\": \\"meta\\", - \\"attributes\\": { - \\"http-equiv\\": \\"X-UA-Compatible\\", - \\"content\\": \\"ie=edge\\" - }, - \\"childNodes\\": [], - \\"id\\": 10 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 11 - }, { \\"type\\": 2, \\"tagName\\": \\"title\\", @@ -9195,24 +9180,24 @@ exports[`record integration tests should record input userTriggered values if us \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"form fields\\", - \\"id\\": 13 + \\"textContent\\": \\"Frame with image\\", + \\"id\\": 11 } ], - \\"id\\": 12 + \\"id\\": 10 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", - \\"id\\": 14 + \\"id\\": 12 } ], \\"id\\": 4 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n\\\\n \\", - \\"id\\": 15 + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 }, { \\"type\\": 2, @@ -9222,620 +9207,1737 @@ exports[`record integration tests should record input userTriggered values if us { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"four\\", + \\"frameborder\\": \\"0\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", \\"id\\": 17 }, { \\"type\\": 2, - \\"tagName\\": \\"form\\", + \\"tagName\\": \\"script\\", \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 20 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"rootId\\": 21, + \\"id\\": 22 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 25 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", + \\"tagName\\": \\"meta\\", \\"attributes\\": { - \\"for\\": \\"text\\" + \\"charset\\": \\"UTF-8\\" }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 21 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"text\\" - }, - \\"childNodes\\": [], - \\"id\\": 22 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 23 - } - ], - \\"id\\": 20 + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 26 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 24 + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 27 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 26 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"radio\\", - \\"name\\": \\"toggle\\", - \\"value\\": \\"on\\" - }, - \\"childNodes\\": [], - \\"id\\": 27 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 28 - } - ], - \\"id\\": 25 + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 28 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, \\"id\\": 29 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 31 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"radio\\", - \\"name\\": \\"toggle\\", - \\"value\\": \\"off\\", - \\"checked\\": true - }, - \\"childNodes\\": [], - \\"id\\": 32 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"Image with blob:url\\", + \\"rootId\\": 21, \\"id\\": 33 } ], - \\"id\\": 30 + \\"rootId\\": 21, + \\"id\\": 32 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, \\"id\\": 34 + } + ], + \\"rootId\\": 21, + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 35 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 37 }, { \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"checkbox\\" - }, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 36 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"checkbox\\" - }, - \\"childNodes\\": [], - \\"id\\": 37 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 38 + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 21, + \\"id\\": 39 } ], - \\"id\\": 35 + \\"rootId\\": 21, + \\"id\\": 38 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 39 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"textarea\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 41 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"textarea\\", - \\"attributes\\": { - \\"name\\": \\"\\", - \\"id\\": \\"\\", - \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" - }, - \\"childNodes\\": [], - \\"id\\": 42 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 43 - } - ], + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 21, \\"id\\": 40 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 44 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"select\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 46 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"select\\", - \\"attributes\\": { - \\"name\\": \\"\\", - \\"id\\": \\"\\", - \\"value\\": \\"1\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 48 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"option\\", - \\"attributes\\": { - \\"value\\": \\"1\\", - \\"selected\\": true - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"1\\", - \\"id\\": 50 - } - ], - \\"id\\": 49 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 51 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"option\\", - \\"attributes\\": { - \\"value\\": \\"2\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"2\\", - \\"id\\": 53 - } - ], - \\"id\\": 52 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 54 - } - ], - \\"id\\": 47 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 55 - } - ], - \\"id\\": 45 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 56 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"password\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 58 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"password\\" - }, - \\"childNodes\\": [], - \\"id\\": 59 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 60 - } - ], - \\"id\\": 57 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 61 - } - ], - \\"id\\": 18 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 62 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"script\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 64 } ], - \\"id\\": 63 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 65 + \\"rootId\\": 21, + \\"id\\": 36 } ], - \\"id\\": 16 + \\"rootId\\": 21, + \\"id\\": 23 } ], - \\"id\\": 3 + \\"id\\": 21 } - ], - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 36, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 41 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"t\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 41, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"te\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 41, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record images inside iframe with blob url after iframe was reloaded 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"tes\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"test\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 22 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 27 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 22 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 27 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 27 + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Frame 2\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n frame 2\\\\n \\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 20 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 21 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 27 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"five\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"userTriggered\\": true, - \\"id\\": 27 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 26 + } + ], + \\"rootId\\": 23, + \\"id\\": 24 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 23 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"off\\", - \\"isChecked\\": false, - \\"userTriggered\\": false, - \\"id\\": 32 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 37 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 27 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 37 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"rootId\\": 27, + \\"id\\": 28 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 33 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 34 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 35 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 36 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 37 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Image with blob:url\\", + \\"rootId\\": 27, + \\"id\\": 39 + } + ], + \\"rootId\\": 27, + \\"id\\": 38 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 40 + } + ], + \\"rootId\\": 27, + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 27, + \\"id\\": 43 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 27, + \\"id\\": 45 + } + ], + \\"rootId\\": 27, + \\"id\\": 44 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 27, + \\"id\\": 46 + } + ], + \\"rootId\\": 27, + \\"id\\": 42 + } + ], + \\"rootId\\": 27, + \\"id\\": 29 + } + ], + \\"id\\": 27 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 42, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 47 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 47, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"userTriggered\\": true, - \\"id\\": 37 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 47, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record images with blob url 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 37 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 59 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"**\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"***\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Image with blob:url\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 22 + } + ], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 23 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"****\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"src\\": \\"blob:http://localhost:3030/...\\", + \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + }, + \\"childNodes\\": [], + \\"id\\": 24 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*****\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 24, + \\"attributes\\": { + \\"crossorigin\\": \\"anonymous\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"******\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 24, + \\"attributes\\": { + \\"crossorigin\\": null + } + } + ], + \\"removes\\": [], + \\"adds\\": [] } - }, + } +]" +`; + +exports[`record integration tests should record input userTriggered values if userTriggeredOnInput is enabled 1`] = ` +"[ { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"*******\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } + \\"type\\": 0, + \\"data\\": {} }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"********\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 59 - } + \\"type\\": 1, + \\"data\\": {} }, { - \\"type\\": 3, + \\"type\\": 4, \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 59 + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 } }, { - \\"type\\": 3, + \\"type\\": 2, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 42 - } - }, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"form fields\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + } + ], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 26 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 27 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 28 + } + ], + \\"id\\": 25 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + } + ], + \\"id\\": 35 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 39 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\" + }, + \\"childNodes\\": [], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + } + ], + \\"id\\": 40 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 44 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\", + \\"selected\\": true + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 50 + } + ], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 53 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 54 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 45 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 61 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 62 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 64 + } + ], + \\"id\\": 63 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 65 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tes\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"test\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"userTriggered\\": true, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"off\\", + \\"isChecked\\": false, + \\"userTriggered\\": false, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"userTriggered\\": true, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 42 + } + }, { \\"type\\": 3, \\"data\\": { @@ -9939,41 +11041,350 @@ exports[`record integration tests should record input userTriggered values if us { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea te\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 5, + \\"text\\": \\"textarea te\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea tes\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea test\\", + \\"isChecked\\": false, + \\"userTriggered\\": true, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"1\\", + \\"isChecked\\": false, + \\"userTriggered\\": false, + \\"id\\": 47 + } + } +]" +`; + +exports[`record integration tests should record mutations in iframes accross pages 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Frame 2\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n frame 2\\\\n \\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 20 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 21 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"five\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + } + } + ] } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea tes\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 23, + \\"id\\": 26 + } + ], + \\"rootId\\": 23, + \\"id\\": 24 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 23 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"textarea test\\", - \\"isChecked\\": false, - \\"userTriggered\\": true, - \\"id\\": 42 + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 22, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 30 + } + ], + \\"rootId\\": 27, + \\"id\\": 28 + } + ], + \\"id\\": 27 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 5, - \\"text\\": \\"1\\", - \\"isChecked\\": false, - \\"userTriggered\\": false, - \\"id\\": 47 + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 30, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 31 + } + } + ] } } ]" diff --git a/packages/rrweb/test/__snapshots__/replayer.test.ts.snap b/packages/rrweb/test/__snapshots__/replayer.test.ts.snap index 45f9b9e3d4..662340595b 100644 --- a/packages/rrweb/test/__snapshots__/replayer.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/replayer.test.ts.snap @@ -81,13 +81,80 @@ file-cid-3 `; exports[`replayer can fast forward past StyleSheetRule deletion on virtual elements 1`] = ` -"file-frame-0 +"file-frame-4 - + +
+
+ +
+ + + + +file-frame-5 + + + + + + + + + + + string + + + +file-cid-0 +@charset \\"utf-8\\"; + +.rr-block { background: currentcolor; } + +noscript { display: none !important; } + +html.rrweb-paused *, html.rrweb-paused ::before, html.rrweb-paused ::after { animation-play-state: paused !important; } + + +file-cid-1 +@charset \\"utf-8\\"; + +.css-added-at-400 { padding: 1.3125rem; flex: 0 0 auto; width: 100%; } + + +file-cid-2 +@charset \\"utf-8\\"; + +.css-added-at-200-overwritten-at-3000 { opacity: 1; transform: translateX(0px); } + +.css-added-at-500-overwritten-at-3000 { border: 1px solid blue; } + + +file-cid-3 +@charset \\"utf-8\\"; + +.css-added-at-200 { position: fixed; top: 0px; right: 0px; left: 4rem; z-index: 15; flex-shrink: 0; height: 0.25rem; overflow: hidden; background-color: rgb(17, 171, 209); } + +.css-added-at-200.alt { height: 0.25rem; background-color: rgb(190, 232, 242); opacity: 0; transition: opacity 0.5s ease 0s; } + +.css-added-at-200.alt2 { padding-left: 4rem; } " `; diff --git a/packages/rrweb/test/html/assets/robot.png b/packages/rrweb/test/html/assets/robot.png new file mode 100644 index 0000000000000000000000000000000000000000..cc486cc8b70680b0dfd72828b6530e0a4e9183ad GIT binary patch literal 11004 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 qTg~2LrAnpQZV>`Mi|_sS + + + + + Frame with image + + + + + diff --git a/packages/rrweb/test/html/image-blob-url.html b/packages/rrweb/test/html/image-blob-url.html new file mode 100644 index 0000000000..4dd3f60855 --- /dev/null +++ b/packages/rrweb/test/html/image-blob-url.html @@ -0,0 +1,21 @@ + + + + + + + Image with blob:url + + + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 3aec801c86..0da1ebdb12 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -499,6 +499,57 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should record images with blob url', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + page.setContent( + getHtml.call(this, 'image-blob-url.html', { inlineImages: true }), + ); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); + await page.waitForSelector('img'); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + it('should record images inside iframe with blob url', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + await page.setContent( + getHtml.call(this, 'frame-image-blob-url.html', { inlineImages: true }), + ); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); + await page.waitForTimeout(50); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + + it('should record images inside iframe with blob url after iframe was reloaded', async () => { + const page: puppeteer.Page = await browser.newPage(); + page.on('console', (msg) => console.log(msg.text())); + await page.goto(`${serverURL}/html`); + await page.setContent( + getHtml.call(this, 'frame2.html', { inlineImages: true }), + ); + await page.waitForSelector('iframe'); // wait for iframe to get added + await waitForRAF(page); // wait for iframe to load + page.evaluate(() => { + const iframe = document.querySelector('iframe')!; + iframe.setAttribute('src', '/html/image-blob-url.html'); + }); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); // wait for image to get loaded + await page.waitForTimeout(50); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('should record shadow DOM', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); @@ -589,6 +640,34 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should record mutations in iframes accross pages', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`${serverURL}/html`); + page.on('console', (msg) => console.log(msg.text())); + await page.setContent(getHtml.call(this, 'frame2.html')); + + await page.waitForSelector('iframe'); // wait for iframe to get added + await waitForRAF(page); // wait for iframe to load + + page.evaluate((serverURL) => { + const iframe = document.querySelector('iframe')!; + iframe.setAttribute('src', `${serverURL}/html`); // load new page + }, serverURL); + + await page.waitForResponse(`${serverURL}/html`); // wait for iframe to load pt1 + await waitForRAF(page); // wait for iframe to load pt2 + + await page.evaluate(() => { + const iframeDocument = document.querySelector('iframe')!.contentDocument!; + const div = iframeDocument.createElement('div'); + iframeDocument.body.appendChild(div); + }); + + await waitForRAF(page); // wait for snapshot to be updated + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + // https://github.com/webcomponents/polyfills/tree/master/packages/shadydom it('should record shadow doms polyfilled by shadydom', async () => { const page: puppeteer.Page = await browser.newPage(); diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index c0c8a07827..e011c532dd 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -160,11 +160,7 @@ describe('replayer', function () { ).length, ); - await assertDomSnapshot( - page, - __filename, - 'style-sheet-rule-events-play-at-1500', - ); + await assertDomSnapshot(page); }); it('should apply fast forwarded StyleSheetRules that where added', async () => { @@ -196,11 +192,7 @@ describe('replayer', function () { ).length, ); - await assertDomSnapshot( - page, - __filename, - 'style-sheet-remove-events-play-at-2500', - ); + await assertDomSnapshot(page); }); it('can restore selection', async () => { @@ -221,11 +213,14 @@ describe('replayer', function () { it('can fast forward past StyleSheetRule deletion on virtual elements', async () => { await page.evaluate(`events = ${JSON.stringify(styleSheetRuleEvents)}`); - await assertDomSnapshot( - page, - __filename, - 'style-sheet-rule-events-play-at-2500', - ); + const actionLength = await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(2600); + replayer['timer']['actions'].length; + `); + + await assertDomSnapshot(page); }); it('should delete fast forwarded StyleSheetRules that where removed', async () => { @@ -676,7 +671,7 @@ describe('replayer', function () { `); await page.waitForTimeout(50); - await assertDomSnapshot(page, __filename, 'ordering-events'); + await assertDomSnapshot(page); }); it('replays same timestamp events in correct order (with addAction)', async () => { @@ -690,6 +685,6 @@ describe('replayer', function () { `); await page.waitForTimeout(50); - await assertDomSnapshot(page, __filename, 'ordering-events'); + await assertDomSnapshot(page); }); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index eb271598b9..5fd249b52a 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -152,20 +152,54 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript } } + + // strip blob:urls as they are different every time + console.log( + a.attributes.src, + 'src' in a.attributes && + a.attributes.src && + typeof a.attributes.src === 'string', + ); }); s.data.adds.forEach((add) => { - if ( - add.node.type === NodeType.Element && - 'style' in add.node.attributes && - typeof add.node.attributes.style === 'string' && - coordinatesReg.test(add.node.attributes.style) - ) { - add.node.attributes.style = add.node.attributes.style.replace( - coordinatesReg, - '$1: Npx', - ); + if (add.node.type === NodeType.Element) { + if ( + 'style' in add.node.attributes && + typeof add.node.attributes.style === 'string' && + coordinatesReg.test(add.node.attributes.style) + ) { + add.node.attributes.style = add.node.attributes.style.replace( + coordinatesReg, + '$1: Npx', + ); + } + coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript + + // strip blob:urls as they are different every time + if ( + 'src' in add.node.attributes && + add.node.attributes.src && + typeof add.node.attributes.src === 'string' && + add.node.attributes.src.startsWith('blob:') + ) { + add.node.attributes.src = add.node.attributes.src.replace( + /[\w-]+$/, + '...', + ); + } + + // strip rr_dataURL as they are not consistent + if ( + 'rr_dataURL' in add.node.attributes && + add.node.attributes.rr_dataURL && + typeof add.node.attributes.rr_dataURL === 'string' + ) { + add.node.attributes.rr_dataURL = add.node.attributes.rr_dataURL.replace( + /,.+$/, + ',...', + ); + } } - coordinatesReg.lastIndex = 0; // wow, a real wart in ECMAScript }); } delete (s as Optional).timestamp; @@ -223,11 +257,7 @@ export function replaceLast(str: string, find: string, replace: string) { return str.substring(0, index) + replace + str.substring(index + find.length); } -export async function assertDomSnapshot( - page: puppeteer.Page, - filename: string, - name: string, -) { +export async function assertDomSnapshot(page: puppeteer.Page) { const cdp = await page.target().createCDPSession(); const { data } = await cdp.send('Page.captureSnapshot', { format: 'mhtml', @@ -555,6 +585,7 @@ export function generateRecordSnippet(options: recordOptions) { userTriggeredOnInput: ${options.userTriggeredOnInput}, maskTextFn: ${options.maskTextFn}, recordCanvas: ${options.recordCanvas}, + inlineImages: ${options.inlineImages}, plugins: ${options.plugins} }); `; From a0d53738f88793fcf088897a5ba6da40ea706ef2 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 11 Aug 2022 13:08:42 +0800 Subject: [PATCH 042/201] fix: errors when fast-forward selection events (#952) --- packages/rrweb/src/replay/index.ts | 72 ++++++++++------ packages/rrweb/test/events/selection.ts | 109 +++++++++++------------- packages/rrweb/test/replayer.test.ts | 22 ++++- 3 files changed, 112 insertions(+), 91 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index df76ca9eeb..2cb4b91d72 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -59,6 +59,7 @@ import { canvasMutationCommand, canvasMutationParam, canvasEventWithTime, + selectionData, } from '../types'; import { polyfill, @@ -142,6 +143,9 @@ export class Replayer { private mousePos: mouseMovePos | null = null; private touchActive: boolean | null = null; + // In the fast-forward mode, only the last selection data needs to be applied. + private lastSelectionData: selectionData | null = null; + constructor( events: Array, config?: Partial, @@ -239,8 +243,12 @@ export class Replayer { true, this.mousePos.debugData, ); + this.mousePos = null; + } + if (this.lastSelectionData) { + this.applySelection(this.lastSelectionData); + this.lastSelectionData = null; } - this.mousePos = null; }); this.emitter.on(ReplayerEvents.PlayBack, () => { this.firstFullSnapshot = null; @@ -1321,32 +1329,11 @@ export class Replayer { break; } case IncrementalSource.Selection: { - const selectionSet = new Set(); - const ranges = d.ranges.map( - ({ start, startOffset, end, endOffset }) => { - const startContainer = this.mirror.getNode(start); - const endContainer = this.mirror.getNode(end); - - if (!startContainer || !endContainer) return; - - const result = new Range(); - - result.setStart(startContainer, startOffset); - result.setEnd(endContainer, endOffset); - const doc = startContainer.ownerDocument; - const selection = doc?.getSelection(); - selection && selectionSet.add(selection); - - return { - range: result, - selection, - }; - }, - ); - - selectionSet.forEach((s) => s.removeAllRanges()); - - ranges.forEach((r) => r && r.selection?.addRange(r.range)); + if (isSync) { + this.lastSelectionData = d; + break; + } + this.applySelection(d); break; } default: @@ -1759,6 +1746,37 @@ export class Replayer { } } + private applySelection(d: selectionData) { + try { + const selectionSet = new Set(); + const ranges = d.ranges.map(({ start, startOffset, end, endOffset }) => { + const startContainer = this.mirror.getNode(start); + const endContainer = this.mirror.getNode(end); + + if (!startContainer || !endContainer) return; + + const result = new Range(); + + result.setStart(startContainer, startOffset); + result.setEnd(endContainer, endOffset); + const doc = startContainer.ownerDocument; + const selection = doc?.getSelection(); + selection && selectionSet.add(selection); + + return { + range: result, + selection, + }; + }); + + selectionSet.forEach((s) => s.removeAllRanges()); + + ranges.forEach((r) => r && r.selection?.addRange(r.range)); + } catch (error) { + // for safe + } + } + private legacy_resolveMissingNode( map: missingNodeMap, parent: Node | RRNode, diff --git a/packages/rrweb/test/events/selection.ts b/packages/rrweb/test/events/selection.ts index 152350b0d4..146cbc60ea 100644 --- a/packages/rrweb/test/events/selection.ts +++ b/packages/rrweb/test/events/selection.ts @@ -45,11 +45,6 @@ const events: eventWithTime[] = [ tagName: 'head', attributes: {}, childNodes: [ - { - type: 3, - textContent: '\\\\n ', - id: 5, - }, { type: 2, tagName: 'meta', @@ -59,49 +54,27 @@ const events: eventWithTime[] = [ childNodes: [], id: 6, }, - { - type: 3, - textContent: '\\\\n ', - id: 7, - }, ], id: 4, }, - { - type: 3, - textContent: '\\\\n ', - id: 8, - }, { type: 2, tagName: 'body', attributes: {}, childNodes: [ - { - type: 3, - textContent: '\\\\n Lorem, ipsum\\\\n ', - id: 10, - }, { type: 2, tagName: 'span', attributes: { id: 'startNode', }, - childNodes: [ - { - type: 3, - textContent: - '\\\\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolores culpa\\\\n corporis voluptas odit nobis recusandae inventore, magni praesentium\\\\n maiores perferendis quaerat excepturi officia minus velit voluptate\\\\n placeat minima? Nesciunt, eum!\\\\n ', - id: 12, - }, - ], + childNodes: [], id: 11, }, { type: 3, textContent: - '\\\\n dolor sit amet consectetur adipisicing elit. Ad repellendus quas hic\\\\n deleniti, delectus consequatur voluptas aliquam dolore voluptates repellat\\\\n perferendis aperiam saepe maxime officia rem corporis beatae, assumenda\\\\n doloribus.\\\\n ', + 'some text between the start node and the end node', id: 13, }, { @@ -110,39 +83,9 @@ const events: eventWithTime[] = [ attributes: { id: 'endNode', }, - childNodes: [ - { - type: 3, - textContent: - '\\\\n Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae\\\\n explicabo omnis dolores magni, ea doloribus possimus debitis reiciendis\\\\n distinctio perferendis nihil ipsum officiis pariatur laboriosam quas,\\\\n corrupti vero vitae minus.\\\\n ', - id: 15, - }, - ], + childNodes: [], id: 14, }, - { - type: 3, - textContent: '\\\\n \\\\n ', - id: 16, - }, - { - type: 2, - tagName: 'script', - attributes: {}, - childNodes: [ - { - type: 3, - textContent: 'SCRIPT_PLACEHOLDER', - id: 18, - }, - ], - id: 17, - }, - { - type: 3, - textContent: '\\\\n \\\\n \\\\n\\\\n', - id: 19, - }, ], id: 9, }, @@ -157,8 +100,54 @@ const events: eventWithTime[] = [ top: 0, }, }, + timestamp: now + 250, + }, + // add selection targets through incremental mutation + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 11, + nextId: null, + node: { + type: 3, + textContent: 'This the text of the start node.', + id: 12, + }, + }, + { + parentId: 14, + nextId: null, + node: { + type: 3, + textContent: 'This the text of the end node.', + id: 15, + }, + }, + ], + }, timestamp: now + 300, }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Selection, + ranges: [ + { + start: 12, + startOffset: 5, + end: 15, + endOffset: 15, + }, + ], + }, + timestamp: now + 350, + }, { type: EventType.IncrementalSnapshot, data: { diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index e011c532dd..bad20d777e 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -195,14 +195,28 @@ describe('replayer', function () { await assertDomSnapshot(page); }); - it('can restore selection', async () => { + it('can fast forward selection events', async () => { await page.evaluate(`events = ${JSON.stringify(selectionEvents)}`); - const [startOffset, endOffset] = (await page.evaluate(` + + /** check the first selection event */ + let [startOffset, endOffset] = (await page.evaluate(` const { Replayer } = rrweb; const replayer = new Replayer(events); - replayer.pause(1500); - const range = replayer.iframe.contentDocument.getSelection().getRangeAt(0); + replayer.pause(360); + var range = replayer.iframe.contentDocument.getSelection().getRangeAt(0); + [range.startOffset, range.endOffset]; + `)) as [startOffset: number, endOffset: number]; + + expect(startOffset).toEqual(5); + expect(endOffset).toEqual(15); + + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + /** check the second selection event */ + [startOffset, endOffset] = (await page.evaluate(` + replayer.pause(410); + var range = replayer.iframe.contentDocument.getSelection().getRangeAt(0); [range.startOffset, range.endOffset]; `)) as [startOffset: number, endOffset: number]; From 5f59f9171e481c78d08a1008186cff31a40ee199 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 11 Aug 2022 19:39:16 +0800 Subject: [PATCH 043/201] feat: add a destroy function to destroy the whole player (#953) * feat: add a destroy function to destroy the whole player * doc: update guidance document * Update packages/rrweb/src/replay/index.ts Co-authored-by: Justin Halsall Co-authored-by: Justin Halsall --- guide.md | 4 ++++ guide.zh_CN.md | 4 ++++ packages/rrweb/src/replay/index.ts | 29 ++++++++++++++++++++-------- packages/rrweb/src/types.ts | 1 + packages/rrweb/test/replayer.test.ts | 17 ++++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/guide.md b/guide.md index 1c52dcb8c0..54e9f9f94e 100644 --- a/guide.md +++ b/guide.md @@ -280,6 +280,9 @@ replayer.pause(); // pause at the fifth seconds replayer.pause(5000); + +// destroy the replayer (hint: this operation is irreversible) +replayer.destroy(); ``` #### Options @@ -385,6 +388,7 @@ The event list: | mouse-interaction | mouse interaction has been replayed | { type, target } | | event-cast | event has been replayed | event | | custom-event | custom event has been replayed | event | +| destroy | destroyed the replayer | - | The rrweb-replayer also re-expose the event listener via a `component.addEventListener` API. diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 054fcb8b12..d69f5c77c6 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -276,6 +276,9 @@ replayer.pause(); // 暂停至第 5 秒处 replayer.pause(5000); + +// 销毁播放器 (提示: 这个操作不可逆) +replayer.destroy(); ``` #### 配置参数 @@ -384,6 +387,7 @@ replayer.on(EVENT_NAME, (payload) => { | mouse-interaction | 回放鼠标交互事件 | { type, target } | | event-cast | 回放 event | event | | custom-event | 回放自定义事件 | event | +| destroy | 销毁播放器 | - | 使用 `rrweb-player` 时,也可以通过 `addEventListener` API 使用相同的事件功能,并且会获得 3 个额外的事件: diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 2cb4b91d72..f368bdafc9 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -322,7 +322,7 @@ export class Replayer { this.rebuildFullSnapshot( firstFullsnapshot as fullSnapshotEvent & { timestamp: number }, ); - this.iframe.contentWindow!.scrollTo( + this.iframe.contentWindow?.scrollTo( (firstFullsnapshot as fullSnapshotEvent).data.initialOffset, ); }, 1); @@ -441,12 +441,22 @@ export class Replayer { public resume(timeOffset = 0) { console.warn( - `The 'resume' will be departed in 1.0. Please use 'play' method which has the same interface.`, + `The 'resume' was deprecated in 1.0. Please use 'play' method which has the same interface.`, ); this.play(timeOffset); this.emitter.emit(ReplayerEvents.Resume); } + /** + * 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.pause(); + this.config.root.removeChild(this.wrapper); + this.emitter.emit(ReplayerEvents.Destroy); + } + public startLive(baselineTime?: number) { this.service.send({ type: 'TO_LIVE', payload: { baselineTime } }); } @@ -590,7 +600,7 @@ export class Replayer { this.firstFullSnapshot = true; } this.rebuildFullSnapshot(event, isSync); - this.iframe.contentWindow!.scrollTo(event.data.initialOffset); + this.iframe.contentWindow?.scrollTo(event.data.initialOffset); }; break; case EventType.IncrementalSnapshot: @@ -611,6 +621,7 @@ export class Replayer { } if (this.isUserInteraction(_event)) { if ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion _event.delay! - event.delay! > SKIP_TIME_THRESHOLD * this.speedService.state.context.timer.speed @@ -622,6 +633,7 @@ export class Replayer { } if (this.nextUserInteractionEvent) { const skipTime = + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.nextUserInteractionEvent.delay! - event.delay!; const payload = { speed: Math.min( @@ -742,7 +754,7 @@ export class Replayer { styleEl, getDefaultSN(styleEl, this.virtualDom.unserializedId), ); - (documentElement as RRElement)!.insertBefore(styleEl, head as RRElement); + (documentElement as RRElement).insertBefore(styleEl, head as RRElement); for (let idx = 0; idx < injectStylesRules.length; idx++) { // push virtual styles styleEl.rules.push({ @@ -753,12 +765,12 @@ export class Replayer { } } else { const styleEl = document.createElement('style'); - (documentElement as HTMLElement)!.insertBefore( + (documentElement as HTMLElement).insertBefore( styleEl, head as HTMLHeadElement, ); for (let idx = 0; idx < injectStylesRules.length; idx++) { - styleEl.sheet!.insertRule(injectStylesRules[idx], idx); + styleEl.sheet?.insertRule(injectStylesRules[idx], idx); } } } @@ -992,6 +1004,7 @@ export class Replayer { doAction() { // }, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion delay: e.delay! - d.positions[0]?.timeOffset, }); } @@ -1708,14 +1721,14 @@ export class Replayer { } const sn = this.mirror.getMeta(target); if (target === this.iframe.contentDocument) { - this.iframe.contentWindow!.scrollTo({ + this.iframe.contentWindow?.scrollTo({ top: d.y, left: d.x, behavior: isSync ? 'auto' : 'smooth', }); } else if (sn?.type === NodeType.Document) { // nest iframe content document - (target as Document).defaultView!.scrollTo({ + (target as Document).defaultView?.scrollTo({ top: d.y, left: d.x, behavior: isSync ? 'auto' : 'smooth', diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index d9a14817cf..7a1779147c 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -743,6 +743,7 @@ export enum ReplayerEvents { Flush = 'flush', StateChange = 'state-change', PlayBack = 'play-back', + Destroy = 'destroy', } export type KeepIframeSrcFn = (src: string) => boolean; diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index bad20d777e..a2453eb68e 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -701,4 +701,21 @@ describe('replayer', function () { await assertDomSnapshot(page); }); + + it('should destroy the replayer after calling destroy()', async () => { + await page.evaluate(`events = ${JSON.stringify(events)}`); + await page.evaluate(` + const { Replayer } = rrweb; + let replayer = new Replayer(events); + replayer.play(); + `); + + const replayerWrapperClassName = 'replayer-wrapper'; + let wrapper = await page.$(`.${replayerWrapperClassName}`); + expect(wrapper).not.toBeNull(); + + await page.evaluate(`replayer.destroy(); replayer = null;`); + wrapper = await page.$(`.${replayerWrapperClassName}`); + expect(wrapper).toBeNull(); + }); }); From 0554408cc091d3667b37eff4d4773f13706fb032 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Fri, 19 Aug 2022 11:59:19 +0200 Subject: [PATCH 044/201] Include eslint jest (#959) --- .eslintrc.js | 3 +- package.json | 3 +- yarn.lock | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2f332b6bbd..2860afba73 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,7 @@ module.exports = { browser: true, es2021: true, node: true, + 'jest/globals': true, }, extends: [ 'eslint:recommended', @@ -16,7 +17,7 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], }, - plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc', 'jest'], rules: { 'tsdoc/syntax': 'warn', }, diff --git a/package.json b/package.json index 9fbd3b7ca6..452d5f18ff 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "@typescript-eslint/eslint-plugin": "^5.25.0", "@typescript-eslint/parser": "^5.25.0", "concurrently": "^7.1.0", - "eslint": "^8.15.0", + "eslint": "^8.19.0", + "eslint-plugin-jest": "^26.5.3", "eslint-plugin-tsdoc": "^0.2.16", "lerna": "^4.0.0", "markdownlint": "^0.25.1", diff --git a/yarn.lock b/yarn.lock index f74e546741..64e609bdf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -517,6 +517,21 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" + integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.2" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" @@ -2371,6 +2386,14 @@ "@typescript-eslint/types" "5.25.0" "@typescript-eslint/visitor-keys" "5.25.0" +"@typescript-eslint/scope-manager@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.4.tgz#8140efd2bc12d41d74e8af23872a89f3edbe552e" + integrity sha512-DNzlQwGSiGefz71JwaHrpcaAX3zYkEcy8uVuan3YMKOa6qeW/y+7SaD8KIsIAruASwq6P+U4BjWBWtM2O+mwBQ== + dependencies: + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/visitor-keys" "5.30.4" + "@typescript-eslint/type-utils@5.23.0": version "5.23.0" resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.23.0.tgz" @@ -2399,6 +2422,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.25.0.tgz#dee51b1855788b24a2eceeae54e4adb89b088dd8" integrity sha512-7fWqfxr0KNHj75PFqlGX24gWjdV/FDBABXL5dyvBOWHpACGyveok8Uj4ipPX/1fGU63fBkzSIycEje4XsOxUFA== +"@typescript-eslint/types@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.4.tgz#3bc99eca8ba3fcfd6a21480e216b09dab81c3999" + integrity sha512-NTEvqc+Vvu8Q6JeAKryHk2eqLKqsr2St3xhIjhOjQv5wQUBhaTuix4WOSacqj0ONWfKVU12Eug3LEAB95GBkMA== + "@typescript-eslint/typescript-estree@5.23.0": version "5.23.0" resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.23.0.tgz" @@ -2425,6 +2453,19 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.4.tgz#ac4be8a2f8fb1f1c3b346d5992a36163121ddb3f" + integrity sha512-V4VnEs6/J9/nNizaA12IeU4SAeEYaiKr7XndLNfV5+3zZSB4hIu6EhHJixTKhvIqA+EEHgBl6re8pivBMLLO1w== + dependencies: + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/visitor-keys" "5.30.4" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.23.0": version "5.23.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.23.0.tgz" @@ -2449,6 +2490,18 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/utils@^5.10.0": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.4.tgz#07a2b7ce80b2527ea506829f190591b76c70ba9f" + integrity sha512-a+GQrJzOUhn4WT1mUumXDyam+22Oo4c5K/jnZ+6r/4WTQF3q8e4CsC9PLHb4SnOClzOqo/5GLZWvkE1aa5UGKQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.30.4" + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/typescript-estree" "5.30.4" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + "@typescript-eslint/visitor-keys@5.23.0": version "5.23.0" resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.23.0.tgz" @@ -2465,6 +2518,14 @@ "@typescript-eslint/types" "5.25.0" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.4.tgz#b4969df1a440cc999d4bb7f7b7932dce05537089" + integrity sha512-ulKGse3mruSc8x6l8ORSc6+1ORyJzKmZeIaRTu/WpaF/jx3vHvEn5XZUKF9XaVg2710mFmTAUlLcLYLPp/Zf/Q== + dependencies: + "@typescript-eslint/types" "5.30.4" + eslint-visitor-keys "^3.3.0" + "@xstate/fsm@^1.4.0": version "1.6.1" resolved "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.1.tgz" @@ -4555,6 +4616,13 @@ eslint-config-google@^0.14.0: resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== +eslint-plugin-jest@^26.5.3: + version "26.5.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" + integrity sha512-sICclUqJQnR1bFRZGLN2jnSVsYOsmPYYnroGCIMVSvTS3y8XR3yjzy1EcTQmk6typ5pRgyIWzbjqxK6cZHEZuQ== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + eslint-plugin-svelte3@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3" @@ -4642,6 +4710,47 @@ eslint@^8.15.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +eslint@^8.19.0: + version "8.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.19.0.tgz#7342a3cbc4fbc5c106a1eefe0fd0b50b6b1a7d28" + integrity sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw== + dependencies: + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.2" + 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 "^6.0.1" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + espree@^9.3.2: version "9.3.2" resolved "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz" @@ -5382,7 +5491,7 @@ globals@^11.1.0: resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: +globals@^13.15.0, globals@^13.6.0, globals@^13.9.0: version "13.15.0" resolved "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz" integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== From 1fb1102eba81548b730844227bf4199817228d8d Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 21 Aug 2022 16:36:06 +0800 Subject: [PATCH 045/201] add more questions for issue template (#965) --- .github/ISSUE_TEMPLATE/bug_report.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 571bc1800d..1fff641682 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -22,6 +22,13 @@ body: - Other (specify below) validations: required: true + - type: input + attributes: + label: Version + description: What's the version of the package? + placeholder: v1.0.0 + validations: + required: true - type: textarea attributes: label: Expected Behavior @@ -34,6 +41,12 @@ body: description: A clear description of what actually happens. validations: required: true + - type: textarea + attributes: + label: Steps to Reproduce + description: Describe detailed steps for reproducing the issue. + validations: + required: true - type: input attributes: label: Testcase Gist URL From 5f3e027604b683ad51ad673adeab1d31dc7dd0e6 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 22 Aug 2022 14:35:51 +0200 Subject: [PATCH 046/201] Create CODE_OF_CONDUCT.md (#968) --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..4e4b2926a2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +core-team@rrweb.io. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. From e0ca9158f8824d3a65b96a33ce5080aefb782711 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 22 Aug 2022 14:36:31 +0200 Subject: [PATCH 047/201] Create CONTRIBUTING.md (#969) --- CONTRIBUTING.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..5ae2f1e1d0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,25 @@ +# Contributing to rrweb +We want to make contributing to this project as easy and transparent as +possible. + +## Our Development Process +The majority of development on rrweb will occur through GitHub. Accordingly, +the process for contributing will follow standard GitHub protocol. + +## Pull Requests +We actively welcome your pull requests. +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints and typechecks. + +## Issues +We use GitHub issues to track public bugs. Please ensure your description is +clear and has sufficient instructions to be able to reproduce the issue. + +## License +rrweb is [MIT licensed](https://github.com/rrweb-io/rrweb/blob/master/LICENSE). + +By contributing to rrweb, you agree that your contributions will be licensed +under its MIT license. From ac7935e378f09fb51375b903a3b98117d2766697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E9=81=87?= <1395294694@qq.com> Date: Sat, 27 Aug 2022 09:04:26 +0800 Subject: [PATCH 048/201] fix: record canvas by fps when blockClass is RegExp (#966) * fix: Fixed a bug where FPSObserver cannot record Canvas when blockClass is a regular expression * Update packages/rrweb/src/record/observers/canvas/canvas-manager.ts Co-authored-by: Justin Halsall --- .../src/record/observers/canvas/canvas-manager.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index 30eb46a6de..22b62358ae 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -9,6 +9,7 @@ import type { listenerHandler, CanvasArg, } from '../../../types'; +import { isBlocked } from '../../../utils'; import { CanvasContext } from '../../../types'; import initCanvas2DMutationObserver from './2d'; import initCanvasContextObserver from './canvas'; @@ -137,6 +138,16 @@ export class CanvasManager { let lastSnapshotTime = 0; let rafId: number; + const getCanvas = (): HTMLCanvasElement[] => { + const matchedCanvas: HTMLCanvasElement[] = []; + win.document.querySelectorAll('canvas').forEach(canvas => { + if (!isBlocked(canvas, blockClass, true)) { + matchedCanvas.push(canvas); + } + }) + return matchedCanvas; + }; + const takeCanvasSnapshots = (timestamp: DOMHighResTimeStamp) => { if ( lastSnapshotTime && @@ -147,8 +158,7 @@ export class CanvasManager { } lastSnapshotTime = timestamp; - win.document - .querySelectorAll(`canvas:not(.${blockClass as string} *)`) + getCanvas() // eslint-disable-next-line @typescript-eslint/no-misused-promises .forEach(async (canvas: HTMLCanvasElement) => { const id = this.mirror.getId(canvas); From 5ba933cce3d9e2acd5e3e82c2936aeecc61958bb Mon Sep 17 00:00:00 2001 From: dbseel Date: Tue, 30 Aug 2022 01:27:24 -0400 Subject: [PATCH 049/201] isBlocked factors in the selector (#894) * isBlocked factors in the selector * Ensure contains parameter is a node * Fix blockSelector blocking for closest nodes * Fix integration test * adding ignoreCSSAttributes to ignore the addition of certain css attributes * tested ignoreCSSAttributes * Update test snapshot * swapped the wrapping of htmlelement to be element * Fix linter errors * Address MR feedback * Rebase Co-authored-by: Filip --- guide.md | 1 + guide.zh_CN.md | 1 + packages/rrweb/src/record/index.ts | 4 +++ packages/rrweb/src/record/mutation.ts | 12 ++++---- packages/rrweb/src/record/observer.ts | 30 +++++++++++++------ .../rrweb/src/record/observers/canvas/2d.ts | 3 +- .../record/observers/canvas/canvas-manager.ts | 17 +++++++---- .../src/record/observers/canvas/canvas.ts | 3 +- .../src/record/observers/canvas/webgl.ts | 6 +++- .../rrweb/src/record/shadow-dom-manager.ts | 2 +- packages/rrweb/src/types.ts | 2 ++ packages/rrweb/src/utils.ts | 10 +++++-- .../test/__snapshots__/record.test.ts.snap | 2 +- packages/rrweb/test/record.test.ts | 9 ++++++ 14 files changed, 74 insertions(+), 28 deletions(-) diff --git a/guide.md b/guide.md index 54e9f9f94e..6d2173df37 100644 --- a/guide.md +++ b/guide.md @@ -143,6 +143,7 @@ The parameter of `rrweb.record` accepts the following options. | blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | | blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | | ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | +| ignoreCSSAttributes | null | array of CSS attributes that should be ignored | | maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | | maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | | maskAllInputs | false | mask all input content as \* | diff --git a/guide.zh_CN.md b/guide.zh_CN.md index d69f5c77c6..926357121d 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -139,6 +139,7 @@ setInterval(save, 10 * 1000); | blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | | blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | | ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | +| ignoreCSSAttributes | null | 应该被忽略的 CSS 属性数组 | | maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | | maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | | maskAllInputs | false | 将所有输入内容记录为 \* | diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 756c21c56b..b566977599 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -70,7 +70,9 @@ function record( inlineImages = false, plugins, keepIframeSrcFn = () => false, + ignoreCSSAttributes = new Set([]), } = options; + // runtime checks for user options if (!emit) { throw new Error('emit function is required'); @@ -226,6 +228,7 @@ function record( mutationCb: wrappedCanvasMutationEmit, win: window, blockClass, + blockSelector, mirror, sampling: sampling.canvas, }); @@ -464,6 +467,7 @@ function record( stylesheetManager, shadowDomManager, canvasManager, + ignoreCSSAttributes, plugins: plugins ?.filter((p) => p.observer) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index df3b3017c2..44ea19fc66 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -273,7 +273,7 @@ export default class MutationBuffer { rootShadowHost = (rootShadowHost?.getRootNode?.() as ShadowRoot | undefined)?.host || null; - // ensure shadowHost is a Node, or doc.contains will throw an error + // ensure contains is passed a Node, or it will throw an error const notInDoc = !this.doc.contains(n) && (!rootShadowHost || !this.doc.contains(rootShadowHost)); @@ -446,7 +446,7 @@ export default class MutationBuffer { case 'characterData': { const value = m.target.textContent; if ( - !isBlocked(m.target, this.blockClass, false) && + !isBlocked(m.target, this.blockClass, this.blockSelector, false) && value !== m.oldValue ) { this.texts.push({ @@ -478,7 +478,7 @@ export default class MutationBuffer { }); } if ( - isBlocked(m.target, this.blockClass, false) || + isBlocked(m.target, this.blockClass, this.blockSelector, false) || value === m.oldValue ) { return; @@ -554,7 +554,7 @@ export default class MutationBuffer { /** * Parent is blocked, ignore all child mutations */ - if (isBlocked(m.target, this.blockClass, true)) return; + if (isBlocked(m.target, this.blockClass, this.blockSelector, true)) return; m.addedNodes.forEach((n) => this.genAdds(n, m.target)); m.removedNodes.forEach((n) => { @@ -563,7 +563,7 @@ export default class MutationBuffer { ? this.mirror.getId(m.target.host) : this.mirror.getId(m.target); if ( - isBlocked(m.target, this.blockClass, false) || + isBlocked(m.target, this.blockClass, this.blockSelector, false) || isIgnored(n, this.mirror) || !isSerialized(n, this.mirror) ) { @@ -635,7 +635,7 @@ export default class MutationBuffer { // if this node is blocked `serializeNode` will turn it into a placeholder element // but we have to remove it's children otherwise they will be added as placeholders too - if (!isBlocked(n, this.blockClass, false)) + if (!isBlocked(n, this.blockClass, this.blockSelector, false)) n.childNodes.forEach((childN) => this.genAdds(childN)); }; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 9350906cec..5322df3cd2 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -210,6 +210,7 @@ function initMouseInteractionObserver({ doc, mirror, blockClass, + blockSelector, sampling, }: observerParam): listenerHandler { if (sampling.mouseInteraction === false) { @@ -227,7 +228,7 @@ function initMouseInteractionObserver({ const getHandler = (eventKey: keyof typeof MouseInteractions) => { return (event: MouseEvent | TouchEvent) => { const target = getEventTarget(event) as Node; - if (isBlocked(target, blockClass, true)) { + if (isBlocked(target, blockClass, blockSelector, true)) { return; } const e = isTouchEvent(event) ? event.changedTouches[0] : event; @@ -266,14 +267,15 @@ export function initScrollObserver({ doc, mirror, blockClass, + blockSelector, sampling, }: Pick< observerParam, - 'scrollCb' | 'doc' | 'mirror' | 'blockClass' | 'sampling' + 'scrollCb' | 'doc' | 'mirror' | 'blockClass' | 'blockSelector' | 'sampling' >): listenerHandler { const updatePosition = throttle((evt) => { const target = getEventTarget(evt); - if (!target || isBlocked(target as Node, blockClass, true)) { + if (!target || isBlocked(target as Node, blockClass, blockSelector, true)) { return; } const id = mirror.getId(target as Node); @@ -331,6 +333,7 @@ function initInputObserver({ doc, mirror, blockClass, + blockSelector, ignoreClass, maskInputOptions, maskInputFn, @@ -350,7 +353,7 @@ function initInputObserver({ !target || !(target as Element).tagName || INPUT_TAGS.indexOf((target as Element).tagName) < 0 || - isBlocked(target as Node, blockClass, true) + isBlocked(target as Node, blockClass, blockSelector, true) ) { return; } @@ -613,7 +616,7 @@ function initStyleSheetObserver( } function initStyleDeclarationObserver( - { styleDeclarationCb, mirror }: observerParam, + { styleDeclarationCb, mirror, ignoreCSSAttributes }: observerParam, { win }: { win: IWindow }, ): listenerHandler { // eslint-disable-next-line @typescript-eslint/unbound-method @@ -624,6 +627,10 @@ function initStyleDeclarationObserver( value: string, priority: string, ) { + // ignore this mutation if we do not care about this css attribute + if (ignoreCSSAttributes.has(property)) { + return setProperty.apply(this, [property, value, priority]); + } const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); if (id !== -1) { styleDeclarationCb({ @@ -645,6 +652,10 @@ function initStyleDeclarationObserver( this: CSSStyleDeclaration, property: string, ) { + // ignore this mutation if we do not care about this css attribute + if (ignoreCSSAttributes.has(property)) { + return removeProperty.apply(this, [property]); + } const id = mirror.getId(this.parentRule?.parentStyleSheet?.ownerNode); if (id !== -1) { styleDeclarationCb({ @@ -667,13 +678,14 @@ function initStyleDeclarationObserver( function initMediaInteractionObserver({ mediaInteractionCb, blockClass, + blockSelector, mirror, sampling, }: observerParam): listenerHandler { const handler = (type: MediaInteractions) => throttle((event: Event) => { const target = getEventTarget(event); - if (!target || isBlocked(target as Node, blockClass, true)) { + if (!target || isBlocked(target as Node, blockClass, blockSelector, true)) { return; } const { currentTime, volume, muted } = target as HTMLMediaElement; @@ -755,7 +767,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { } function initSelectionObserver(param: observerParam): listenerHandler { - const { doc, mirror, blockClass, selectionCb } = param; + const { doc, mirror, blockClass, blockSelector, selectionCb } = param; let collapsed = true; const updateSelection = () => { @@ -774,8 +786,8 @@ function initSelectionObserver(param: observerParam): listenerHandler { const { startContainer, startOffset, endContainer, endOffset } = range; const blocked = - isBlocked(startContainer, blockClass, true) || - isBlocked(endContainer, blockClass, true); + isBlocked(startContainer, blockClass, blockSelector, true) || + isBlocked(endContainer, blockClass, blockSelector, true); if (blocked) continue; diff --git a/packages/rrweb/src/record/observers/canvas/2d.ts b/packages/rrweb/src/record/observers/canvas/2d.ts index 52ba1bf3aa..dd54e55452 100644 --- a/packages/rrweb/src/record/observers/canvas/2d.ts +++ b/packages/rrweb/src/record/observers/canvas/2d.ts @@ -13,6 +13,7 @@ export default function initCanvas2DMutationObserver( cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, + blockSelector: string | null, mirror: Mirror, ): listenerHandler { const handlers: listenerHandler[] = []; @@ -41,7 +42,7 @@ export default function initCanvas2DMutationObserver( this: CanvasRenderingContext2D, ...args: Array ) { - if (!isBlocked(this.canvas, blockClass, true)) { + if (!isBlocked(this.canvas, blockClass, blockSelector, true)) { // Using setTimeout as toDataURL can be heavy // and we'd rather not block the main thread setTimeout(() => { diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index 22b62358ae..5b2a874d01 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -60,17 +60,18 @@ export class CanvasManager { mutationCb: canvasMutationCallback; win: IWindow; blockClass: blockClass; + blockSelector: string | null, mirror: Mirror; sampling?: 'all' | number; }) { - const { sampling = 'all', win, blockClass, recordCanvas } = options; + const { sampling = 'all', win, blockClass, blockSelector, recordCanvas } = options; this.mutationCb = options.mutationCb; this.mirror = options.mirror; if (recordCanvas && sampling === 'all') - this.initCanvasMutationObserver(win, blockClass); + this.initCanvasMutationObserver(win, blockClass, blockSelector); if (recordCanvas && typeof sampling === 'number') - this.initCanvasFPSObserver(sampling, win, blockClass); + this.initCanvasFPSObserver(sampling, win, blockClass, blockSelector); } private processMutation: canvasManagerMutationCallback = ( @@ -94,8 +95,9 @@ export class CanvasManager { fps: number, win: IWindow, blockClass: blockClass, + blockSelector: string | null, ) { - const canvasContextReset = initCanvasContextObserver(win, blockClass); + const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector); const snapshotInProgressMap: Map = new Map(); const worker = new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker; worker.onmessage = (e) => { @@ -141,7 +143,7 @@ export class CanvasManager { const getCanvas = (): HTMLCanvasElement[] => { const matchedCanvas: HTMLCanvasElement[] = []; win.document.querySelectorAll('canvas').forEach(canvas => { - if (!isBlocked(canvas, blockClass, true)) { + if (!isBlocked(canvas, blockClass, blockSelector, true)) { matchedCanvas.push(canvas); } }) @@ -208,15 +210,17 @@ export class CanvasManager { private initCanvasMutationObserver( win: IWindow, blockClass: blockClass, + blockSelector: string | null, ): void { this.startRAFTimestamping(); this.startPendingCanvasMutationFlusher(); - const canvasContextReset = initCanvasContextObserver(win, blockClass); + const canvasContextReset = initCanvasContextObserver(win, blockClass, blockSelector); const canvas2DReset = initCanvas2DMutationObserver( this.processMutation.bind(this), win, blockClass, + blockSelector, this.mirror, ); @@ -224,6 +228,7 @@ export class CanvasManager { this.processMutation.bind(this), win, blockClass, + blockSelector, this.mirror, ); diff --git a/packages/rrweb/src/record/observers/canvas/canvas.ts b/packages/rrweb/src/record/observers/canvas/canvas.ts index 9411b91c7b..c12d93c1cc 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas.ts @@ -5,6 +5,7 @@ import { isBlocked, patch } from '../../../utils'; export default function initCanvasContextObserver( win: IWindow, blockClass: blockClass, + blockSelector: string | null, ): listenerHandler { const handlers: listenerHandler[] = []; try { @@ -23,7 +24,7 @@ export default function initCanvasContextObserver( contextType: string, ...args: Array ) { - if (!isBlocked(this, blockClass, true)) { + if (!isBlocked(this, blockClass, blockSelector, true)) { if (!('__context' in this)) this.__context = contextType; } return original.apply(this, [contextType, ...args]); diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index f4f003f116..9c6a3b7cbd 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -15,6 +15,7 @@ function patchGLPrototype( type: CanvasContext, cb: canvasManagerMutationCallback, blockClass: blockClass, + blockSelector: string | null, mirror: Mirror, win: IWindow, ): listenerHandler[] { @@ -36,7 +37,7 @@ function patchGLPrototype( return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); saveWebGLVar(result, win, prototype); - if (!isBlocked(this.canvas, blockClass, true)) { + if (!isBlocked(this.canvas, blockClass, blockSelector, true)) { const recordArgs = serializeArgs([...args], win, prototype); const mutation: canvasMutationWithType = { type, @@ -76,6 +77,7 @@ export default function initCanvasWebGLMutationObserver( cb: canvasManagerMutationCallback, win: IWindow, blockClass: blockClass, + blockSelector: string | null, mirror: Mirror, ): listenerHandler { const handlers: listenerHandler[] = []; @@ -86,6 +88,7 @@ export default function initCanvasWebGLMutationObserver( CanvasContext.WebGL, cb, blockClass, + blockSelector, mirror, win, ), @@ -98,6 +101,7 @@ export default function initCanvasWebGLMutationObserver( CanvasContext.WebGL2, cb, blockClass, + blockSelector, mirror, win, ), diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index 90a1d321d6..b2aeae01b2 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -39,7 +39,7 @@ export class ShadowDomManager { const manager = this; this.restorePatches.push( patch( - HTMLElement.prototype, + Element.prototype, 'attachShadow', function (original: (init: ShadowRootInit) => ShadowRoot) { return function (this: HTMLElement, option: ShadowRootInit) { diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 7a1779147c..4da6d275a5 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -245,6 +245,7 @@ export type recordOptions = { maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; slimDOMOptions?: SlimDOMOptions | 'all' | true; + ignoreCSSAttributes?:Set; inlineStylesheet?: boolean; hooks?: hooksParam; packFn?: PackFn; @@ -294,6 +295,7 @@ export type observerParam = { stylesheetManager: StylesheetManager; shadowDomManager: ShadowDomManager; canvasManager: CanvasManager; + ignoreCSSAttributes:Set; plugins: Array<{ observer: ( cb: (...arg: Array) => void, diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 2d0838b6f3..209d99d22c 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -187,12 +187,14 @@ export function getWindowWidth(): number { * Checks if the given element set to be blocked by rrweb * @param node - node to check * @param blockClass - class name to check - * @param ignoreParents - whether to search through parent nodes for the block class + * @param blockSelector - css selectors to check + * @param checkAncestors - whether to search through parent nodes for the block class * @returns true/false if the node was blocked or not */ export function isBlocked( node: Node | null, blockClass: blockClass, + blockSelector: string | null, checkAncestors: boolean, ): boolean { if (!node) { @@ -203,13 +205,17 @@ export function isBlocked( ? (node as HTMLElement) : node.parentElement; if (!el) return false; - + if (typeof blockClass === 'string') { if (el.classList.contains(blockClass)) return true; if (checkAncestors && el.closest('.' + blockClass) !== null) return true; } else { if (classMatchesRegex(el, blockClass, checkAncestors)) return true; } + if (blockSelector) { + if ((node as HTMLElement).matches(blockSelector)) return true; + if (checkAncestors && el.closest(blockSelector) !== null) return true; + } return false; } diff --git a/packages/rrweb/test/__snapshots__/record.test.ts.snap b/packages/rrweb/test/__snapshots__/record.test.ts.snap index 2c2b7c8d0d..a86e45dd09 100644 --- a/packages/rrweb/test/__snapshots__/record.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/record.test.ts.snap @@ -581,7 +581,7 @@ exports[`record captures style property changes 1`] = ` \\"source\\": 13, \\"id\\": 9, \\"set\\": { - \\"property\\": \\"color\\", + \\"property\\": \\"border-color\\", \\"value\\": \\"green\\" }, \\"index\\": [ diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index f9b46aea02..f803c3302f 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -385,6 +385,7 @@ describe('record', function (this: ISuite) { record({ emit: ((window as unknown) as IWindow).emit, + ignoreCSSAttributes: new Set(['color']), }); const styleElement = document.createElement('style'); @@ -393,10 +394,18 @@ describe('record', function (this: ISuite) { const styleSheet = styleElement.sheet; styleSheet.insertRule('body { background: #000; }'); setTimeout(() => { + // should be ignored (styleSheet.cssRules[0] as CSSStyleRule).style.setProperty( 'color', 'green', ); + + // should be captured because we did not block it + (styleSheet.cssRules[0] as CSSStyleRule).style.setProperty( + 'border-color', + 'green', + ); + (styleSheet.cssRules[0] as CSSStyleRule).style.removeProperty( 'background', ); From f1b5cb2738527eff44b50ad0cc14d171e006c974 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 6 Sep 2022 09:38:43 +0800 Subject: [PATCH 050/201] CI: add a prettier GitHub action to format code automatically (#988) * CI: add a prettier GitHub action to format code automatically * improve GitHub Action config and format some files * Apply formatting changes * CI: make the prettier action a standalone action * Apply formatting changes * CI: add push as new trigger event Co-authored-by: Mark-Fenng --- .github/workflows/eslint.yml | 2 +- .github/workflows/prettier.yml | 26 +++++++++++++++++++ .release-it.json | 2 +- CODE_OF_CONDUCT.md | 22 ++++++++-------- CONTRIBUTING.md | 6 +++++ docs/observer.zh_CN.md | 6 ++--- docs/recipes/record-and-replay.zh_CN.md | 7 +++-- docs/replay.md | 5 ++++ docs/sandbox.md | 3 +++ docs/sandbox.zh_CN.md | 2 +- docs/serialization.md | 12 +++++---- docs/serialization.zh_CN.md | 12 ++++----- guide.md | 2 +- packages/rrweb-player/README.md | 8 +++--- packages/rrweb/.release-it.json | 2 +- packages/rrweb/src/record/mutation.ts | 3 ++- packages/rrweb/src/record/observer.ts | 5 +++- .../record/observers/canvas/canvas-manager.ts | 26 ++++++++++++++----- packages/rrweb/src/types.ts | 4 +-- packages/rrweb/src/utils.ts | 2 +- packages/rrweb/test/events/ordering.ts | 4 +-- packages/rrweb/test/integration.test.ts | 2 +- 22 files changed, 109 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/prettier.yml diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index a9f7736953..8afae5c7ee 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -1,6 +1,6 @@ name: Eslint Check -on: [pull_request] +on: [pull_request, push] jobs: eslint_check_upload: diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 0000000000..3e0ac3182d --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,26 @@ +name: Prettier + +on: [pull_request, push] + +jobs: + prettier: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'yarn' + - name: Install Dependencies + run: yarn + - name: Prettify code + run: yarn prettier --write '**/*.{ts,md}' + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Apply formatting changes + branch: ${{ github.head_ref }} diff --git a/.release-it.json b/.release-it.json index 41e16cf33f..864f37980b 100644 --- a/.release-it.json +++ b/.release-it.json @@ -12,4 +12,4 @@ "release": true, "releaseName": "Release ${version}" } -} \ No newline at end of file +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 4e4b2926a2..eddf527f60 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ae2f1e1d0..e7a0df1668 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,17 @@ # Contributing to rrweb + We want to make contributing to this project as easy and transparent as possible. ## Our Development Process + The majority of development on rrweb will occur through GitHub. Accordingly, the process for contributing will follow standard GitHub protocol. ## Pull Requests + We actively welcome your pull requests. + 1. Fork the repo and create your branch from `master`. 2. If you've added code that should be tested, add tests 3. If you've changed APIs, update the documentation. @@ -15,10 +19,12 @@ We actively welcome your pull requests. 5. Make sure your code lints and typechecks. ## Issues + We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. ## License + rrweb is [MIT licensed](https://github.com/rrweb-io/rrweb/blob/master/LICENSE). By contributing to rrweb, you agree that your contributions will be licensed diff --git a/docs/observer.zh_CN.md b/docs/observer.zh_CN.md index baaa123c6b..9da4a40d6a 100644 --- a/docs/observer.zh_CN.md +++ b/docs/observer.zh_CN.md @@ -57,8 +57,8 @@ body 在[序列化设计](./serialization.zh_CN.md)中已经介绍了我们需要维护一个 `id -> Node` 的映射,因此当出现新增节点时,我们需要将新节点序列化并加入映射中。但由于我们为了去重新增节点,选择在所有 mutation 记录遍历完毕之后才进行序列化,在以下示例中就会出现问题: -1. mutation 记录1,新增节点 n1。我们暂不处理,等待最终去重后序列化。 -2. mutation 记录2,n1 新增属性 a1。我们试图将它记录成一次增量快照,但会发现无法从映射中找到 n1 对应的 id,因为此时它还未被序列化。 +1. mutation 记录 1,新增节点 n1。我们暂不处理,等待最终去重后序列化。 +2. mutation 记录 2,n1 新增属性 a1。我们试图将它记录成一次增量快照,但会发现无法从映射中找到 n1 对应的 id,因为此时它还未被序列化。 由此可见,由于我们对新增节点进行了延迟序列化的处理,所有 mutation 记录也都需要先收集,再新增节点去重并序列化之后再做处理。 @@ -119,4 +119,4 @@ function hookSetter( } ``` -注意为了避免我们在 setter 中的逻辑阻塞被录制页面的正常交互,我们应该把逻辑放入 event loop 中异步执行。 \ No newline at end of file +注意为了避免我们在 setter 中的逻辑阻塞被录制页面的正常交互,我们应该把逻辑放入 event loop 中异步执行。 diff --git a/docs/recipes/record-and-replay.zh_CN.md b/docs/recipes/record-and-replay.zh_CN.md index 0c103fc1ec..6661cffba3 100644 --- a/docs/recipes/record-and-replay.zh_CN.md +++ b/docs/recipes/record-and-replay.zh_CN.md @@ -8,8 +8,8 @@ const stopFn = rrweb.record({ emit(event) { // 保存获取到的 event 数据 - } -}) + }, +}); ``` 你可以使用任何方式保存录制的数据,例如通过网络请求将数据传入至后端持久化保存,但请确保: @@ -22,9 +22,8 @@ const stopFn = rrweb.record({ 回放时只需要获取一段录制数据,并传入 rrweb 提供的 Replayer: ```js -const events = GET_YOUR_EVENTS +const events = GET_YOUR_EVENTS; const replayer = new rrweb.Replayer(events); replayer.play(); ``` - diff --git a/docs/replay.md b/docs/replay.md index 9c200491ca..662935046e 100644 --- a/docs/replay.md +++ b/docs/replay.md @@ -1,7 +1,9 @@ # Replay + A design principle of rrweb is to process as little as possible on the recording side, minimizing the impact on the recorded page. This means we need to do some special processing on the replay side. ## High precision timer + During replay, we will get the complete snapshot chain at one time. If all the snapshots are executed in sequence, we can directly get the last state of the recorded page, but what we need is to synchronously initialize the first full snapshot, and then apply the remaining incremental snapshots asynchronously. Using a time interval we replay each incremental snapshot one after the other, which requires a high-precision timer. The reason why **high precision** is emphasized is because the native `setTimeout` does not guarantee accurate execution after the set delay time, for example, when the main thread is blocked. @@ -11,6 +13,7 @@ For our replay function, this imprecise delay is unacceptable and can lead to va At the same time, the custom timer is also the basis for our "fast forward" function. ## Completing missing nodes + The delay serialization strategy when rrweb uses MutationObserver is mentioned in the [incremental snapshot design](./observer.md), which may result in the following scenarios where we cannot record a full incremental snapshot: ``` @@ -29,6 +32,7 @@ During replay, when we process the incremental snapshot of the new `foo`, we kno After processing the incremental snapshot of the new n1, we normally process and insert `bar`. After the replay is completed, we check whether the neighbor node id of `foo` points to a node which is in the missing node pool. If it matches, then it will be removed from the pool and be inserted into the DOM tree. ## Simulation Hover + CSS styles for the `:hover` selector are present in many web pages, but we can't trigger the hover state via JavaScript. So when playing back we need to simulate the hover state to make the style display correctly. The specific method includes two parts: @@ -37,6 +41,7 @@ The specific method includes two parts: 2. When playing back the mouse up mouse interaction event, add the `.:hover` class name to the event target and all its ancestors, and remove it when the mouse moves away again. ## Play from any point in time + In addition to the basic replay features, we also want players like `rrweb-player` to provide similar functionality to video players, such as dragging and dropping to the progress bar to any point in time. In actual implementation, we pass a start time to the method. We can then divide the snapshot chain into two parts: The parts before and the part after the start time. Then, the snapshot chain before the start time is executed synchronously, and then the snapshot chain after the starting times uses the normal asynchronous execution. This way we can achieve starting replay from any point in time. diff --git a/docs/sandbox.md b/docs/sandbox.md index de9b0dbd94..4e7f396d44 100644 --- a/docs/sandbox.md +++ b/docs/sandbox.md @@ -5,6 +5,7 @@ In the [serialization design](./serialization.md) we mentioned the "de-scripting There are many kinds of scripting behaviors. A filtering approach to getting rid of these scripts will never be a complete solution, and once a script slips through and is executed, it may cause irreversible unintended consequences. So we use the iframe sandbox feature provided by HTML for browser-level restrictions. ## iframe sandbox + We reconstruct the recorded DOM in an `iframe` element when we rebuild the snapshot. By setting its `sandbox` attribute, we can disable the following behavior: - Form submission @@ -14,6 +15,7 @@ We reconstruct the recorded DOM in an `iframe` element when we rebuild the snaps This is in line with our expectations, especially when dealing with JS scripts is safer and more reliable than implementing this security ourselves. ## Avoid link jumps + When you click the a element link, the default event is to jump to the URL corresponding to its href attribute. During replay, we will ensure visually correct replay by rebuilding the page DOM after the jump, and the original jump should be prohibited. Usually we will capture all an elements click events through the event handler proxy and disable the default event via `event.preventDefault()`. But when we put the replay page in the sandbox, all the event handlers will not be executed, and we will not be able to implement the event delegation. @@ -21,6 +23,7 @@ Usually we will capture all an elements click events through the event handler p When replaying interactive events, note that replaying the JS `click` event is not nessecary because click events do not have any impact when JS is disabled. However, in order to optimize the replay effect, we can add special animation effects to visualize elements being clicked with the mouse, to clearly show the viewer that a click has occurred. ## iframe style settings + Since we're rebuilding the DOM in an iframe, we can't affect the elements in the iframe through the CSS stylesheet of the parent page. But if JS scripts are not allowed to execute, the `noscript` tag will be displayed, and we want to hide it. So we need to dynamically add styles to the iframe. The sample code is as follows: ```typescript diff --git a/docs/sandbox.zh_CN.md b/docs/sandbox.zh_CN.md index 32f847b39d..57ba2e614b 100644 --- a/docs/sandbox.zh_CN.md +++ b/docs/sandbox.zh_CN.md @@ -42,4 +42,4 @@ for (let idx = 0; idx < injectStyleRules.length; idx++) { } ``` -需要注意的是这个插入的 style 元素在被录制页面中并不存在,所以我们不能将其序列化,否则 `id -> Node` 的映射将出现错误。 \ No newline at end of file +需要注意的是这个插入的 style 元素在被录制页面中并不存在,所以我们不能将其序列化,否则 `id -> Node` 的映射将出现错误。 diff --git a/docs/serialization.md b/docs/serialization.md index c34feee64e..9242316489 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -1,4 +1,5 @@ # Serialization + If you only need to record and replay changes within the browser locally, then we can simply save the current view by deep copying the DOM object. For example, the following code implementation (simplified example with jQuery, saves only the body part): ```javascript @@ -18,6 +19,7 @@ We do not use an existing open source solutions such as [parse5](https://github. 2. This part of the code needs to run on the recorded page, and we want to control the amount of code as much as possible, only retaining the necessary functions. ## Special handling in serialization + The reason why our serialization method is non-standard is because we still need to do the following parts: 1. Output needs to be descriptive. All JavaScript in the original recorded page should not be executed on replay. In rrweb we do this by replacing `script` tags with placeholder `noscript` tags in snapshots. The content inside the script is no longer important. We instead record any changes to the DOM that scripts cause, and we ​​do not need to fully record large amounts of script content that may be present on the original web page. @@ -26,6 +28,7 @@ The reason why our serialization method is non-standard is because we still need 4. We want to record the contents of the CSS style sheet. If the recorded page links to external style sheets, we can get its parsed CSS rules from the browser, generate an inline style sheet containing all these rules. This way stylesheets that are not always accessible (for example, because they are located on an intranet or localhost) are included in the recording and can be replayed correctly. ## Uniquely identifies + At the same time, our serialization should also include both full and incremental types. Full serialization can transform a DOM tree into a corresponding tree data structure. For example, the following DOM tree: @@ -33,8 +36,7 @@ For example, the following DOM tree: ```html -
-
+
``` @@ -98,12 +100,12 @@ There are two things to note in this serialization result: Imagine if we recorded the click of a button on the same page and played it back, we can record the operation in the following format (that is what we call an incremental snapshot): -```javascript +```typescript type clickSnapshot = { source: 'MouseInteraction'; type: 'Click'; node: HTMLButtonElement; -} +}; ``` The operation can be executed again by `snapshot.node.click()`. @@ -119,5 +121,5 @@ type clickSnapshot = { source: 'MouseInteraction'; type: 'Click'; id: Number; -} +}; ``` diff --git a/docs/serialization.zh_CN.md b/docs/serialization.zh_CN.md index 76d029d22f..150acd4342 100644 --- a/docs/serialization.zh_CN.md +++ b/docs/serialization.zh_CN.md @@ -22,7 +22,7 @@ $('body').replaceWith(snapshot); 1. 去脚本化。被录制页面中的所有 JavaScript 都不应该被执行,例如我们会在重建快照时将 `script` 标签改为 `noscript` 标签,此时 script 内部的内容就不再重要,录制时可以简单记录一个标记值而不需要将可能存在的大量脚本内容全部记录。 2. 记录没有反映在 HTML 中的视图状态。例如 `` 输入后的值不会反映在其 HTML 中,而是通过 `value` 属性记录,我们在序列化时就需要读出该值并且以属性的形式回放成 ``。 -3. 相对路径转换为绝对路径。回放时我们会将被录制的页面放置在一个 ` + `; + + const sheet = new CSSStyleSheet(); + // Add stylesheet to a document. + + document.adoptedStyleSheets = [sheet]; + + const iframe = document.querySelector('iframe'); + const sheet2 = new (iframe!.contentWindow! as Window & + typeof globalThis).CSSStyleSheet(); + + // Add stylesheet to an IFrame document. + iframe!.contentDocument!.adoptedStyleSheets = [sheet2]; + iframe!.contentDocument!.body.innerHTML = '

h1 in iframe

'; + + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + emit: ((window as unknown) as IWindow).emit, + }); + + setTimeout(() => { + sheet.replace!('div { color: yellow; }'); + sheet2.replace!('h1 { color: blue; }'); + }, 0); + + setTimeout(() => { + sheet.replaceSync!('div { display: inline ; }'); + sheet2.replaceSync!('h1 { font-size: large; }'); + }, 5); + + setTimeout(() => { + (sheet.cssRules[0] as CSSStyleRule).style.setProperty('color', 'green'); + (sheet.cssRules[0] as CSSStyleRule).style.removeProperty('display'); + (sheet2.cssRules[0] as CSSStyleRule).style.setProperty( + 'font-size', + 'medium', + 'important', + ); + sheet2.insertRule('h2 { color: red; }'); + }, 10); + + setTimeout(() => { + sheet.insertRule('body { border: 2px solid blue; }', 1); + sheet2.deleteRule(0); + }, 15); + }); + await waitForRAF(ctx.page); + assertSnapshot(ctx.events); + }); + + it('captures adopted stylesheets in nested shadow doms and iframes', async () => { + await ctx.page.evaluate(() => { + document.body.innerHTML = ` +
entry
+ `; + + let shadowHost = document.querySelector('div')!; + shadowHost!.attachShadow({ mode: 'open' }); + let iframeDocument: Document; + const NestedDepth = 4; + // construct nested shadow doms and iframe elements + for (let i = 1; i <= NestedDepth; i++) { + const shadowRoot = shadowHost.shadowRoot!; + const iframeElement = document.createElement('iframe'); + shadowRoot.appendChild(iframeElement); + iframeElement.id = `iframe-${i}`; + iframeDocument = iframeElement.contentDocument!; + shadowHost = iframeDocument.createElement('div'); + shadowHost.id = `shadow-host-${i + 1}`; + iframeDocument.body.append(shadowHost); + shadowHost!.attachShadow({ mode: 'open' }); + } + + const iframeWin = iframeDocument!.defaultView!; + const sheet1 = new iframeWin.CSSStyleSheet(); + sheet1.replaceSync!('h1 {color: blue;}'); + iframeDocument!.adoptedStyleSheets = [sheet1]; + const sheet2 = new iframeWin.CSSStyleSheet(); + sheet2.replaceSync!('div {font-size: large;}'); + shadowHost.shadowRoot!.adoptedStyleSheets = [sheet2]; + + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + emit: ((window as unknown) as IWindow).emit, + }); + + setTimeout(() => { + sheet1.insertRule!('div { display: inline ; }', 1); + sheet2.replaceSync!('h1 { font-size: large; }'); + }, 100); + + setTimeout(() => { + const sheet3 = new iframeWin.CSSStyleSheet(); + sheet3.replaceSync!('span {background-color: red;}'); + iframeDocument!.adoptedStyleSheets = [sheet3, sheet2]; + shadowHost.shadowRoot!.adoptedStyleSheets = [sheet1, sheet3]; + }, 150); + }); + await ctx.page.waitForTimeout(200); + assertSnapshot(ctx.events); + }); + it('captures stylesheets in iframes with `blob:` url', async () => { await ctx.page.evaluate(() => { const iframe = document.createElement('iframe'); @@ -579,6 +687,73 @@ describe('record', function (this: ISuite) { assertSnapshot(ctx.events); }); + + it('captures adopted stylesheets in shadow doms and iframe', async () => { + await ctx.page.evaluate(() => { + document.body.innerHTML = ` +
div in outermost document
+
+
+ + `; + + const sheet = new CSSStyleSheet(); + sheet.replaceSync!( + 'div { color: yellow; } h2 { color: orange; } h3 { font-size: larger;}', + ); + // Add stylesheet to a document. + + document.adoptedStyleSheets = [sheet]; + + // Add stylesheet to a shadow host. + const host = document.querySelector('#shadow-host1'); + const shadow = host!.attachShadow({ mode: 'open' }); + shadow.innerHTML = + '
div in shadow dom 1
span in shadow dom 1'; + const sheet2 = new CSSStyleSheet(); + + sheet2.replaceSync!('span { color: red; }'); + + shadow.adoptedStyleSheets = [sheet, sheet2]; + + // Add stylesheet to an IFrame document. + const iframe = document.querySelector('iframe'); + const sheet3 = new (iframe!.contentWindow! as IWindow & + typeof globalThis).CSSStyleSheet(); + sheet3.replaceSync!('h1 { color: blue; }'); + + iframe!.contentDocument!.adoptedStyleSheets = [sheet3]; + + const ele = iframe!.contentDocument!.createElement('h1'); + ele.innerText = 'h1 in iframe'; + iframe!.contentDocument!.body.appendChild(ele); + + ((window as unknown) as IWindow).rrweb.record({ + emit: ((window.top as unknown) as IWindow).emit, + }); + + // Make incremental changes to shadow dom. + setTimeout(() => { + const host = document.querySelector('#shadow-host2'); + const shadow = host!.attachShadow({ mode: 'open' }); + shadow.innerHTML = + '
div in shadow dom 2
span in shadow dom 2'; + const sheet4 = new CSSStyleSheet(); + sheet4.replaceSync!('span { color: green; }'); + shadow.adoptedStyleSheets = [sheet, sheet4]; + + document.adoptedStyleSheets = [sheet4, sheet, sheet2]; + + const sheet5 = new (iframe!.contentWindow! as IWindow & + typeof globalThis).CSSStyleSheet(); + sheet5.replaceSync!('h2 { color: purple; }'); + iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3]; + }, 10); + }); + await waitForRAF(ctx.page); // wait till events get sent + + assertSnapshot(ctx.events); + }); }); describe('record iframes', function (this: ISuite) { diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index d7a6944caf..7bc09ae2ca 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -1,6 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import type * as puppeteer from 'puppeteer'; +import 'construct-style-sheets-polyfill'; import { assertDomSnapshot, launchPuppeteer, @@ -17,6 +18,8 @@ import selectionEvents from './events/selection'; import shadowDomEvents from './events/shadow-dom'; import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; +import adoptedStyleSheet from './events/adopted-style-sheet'; +import adoptedStyleSheetModification from './events/adopted-style-sheet-modification'; interface ISuite { code: string; @@ -718,4 +721,232 @@ describe('replayer', function () { wrapper = await page.$(`.${replayerWrapperClassName}`); expect(wrapper).toBeNull(); }); + + it('can replay adopted stylesheet events', async () => { + await page.evaluate(` + events = ${JSON.stringify(adoptedStyleSheet)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.play(); + `); + await page.waitForTimeout(600); + const iframe = await page.$('iframe'); + const contentDocument = await iframe!.contentFrame()!; + const colorRGBMap = { + yellow: 'rgb(255, 255, 0)', + red: 'rgb(255, 0, 0)', + blue: 'rgb(0, 0, 255)', + green: 'rgb(0, 128, 0)', + }; + const checkCorrectness = async () => { + // check the adopted stylesheet is applied on the outermost document + expect( + await contentDocument!.$eval( + 'div', + (element) => window.getComputedStyle(element).color, + ), + ).toEqual(colorRGBMap.yellow); + + // check the adopted stylesheet is applied on the shadow dom #1's root + expect( + await contentDocument!.evaluate( + () => + window.getComputedStyle( + document + .querySelector('#shadow-host1')! + .shadowRoot!.querySelector('span')!, + ).color, + ), + ).toEqual(colorRGBMap.red); + + // check the adopted stylesheet is applied on document of the IFrame element + expect( + await contentDocument!.$eval( + 'iframe', + (element) => + window.getComputedStyle( + (element as HTMLIFrameElement).contentDocument!.querySelector( + 'h1', + )!, + ).color, + ), + ).toEqual(colorRGBMap.blue); + + // check the adopted stylesheet is applied on the shadow dom #2's root + expect( + await contentDocument!.evaluate( + () => + window.getComputedStyle( + document + .querySelector('#shadow-host2')! + .shadowRoot!.querySelector('span')!, + ).color, + ), + ).toEqual(colorRGBMap.green); + }; + await checkCorrectness(); + + // To test the correctness of replaying adopted stylesheet events in the fast-forward mode. + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(600);'); + await checkCorrectness(); + }); + + it('can replay modification events for adoptedStyleSheet', async () => { + await page.evaluate(` + events = ${JSON.stringify(adoptedStyleSheetModification)}; + const { Replayer } = rrweb; + var replayer = new Replayer(events,{showDebug:true}); + replayer.play(); + `); + const iframe = await page.$('iframe'); + const contentDocument = await iframe!.contentFrame()!; + + // At 250ms, the adopted stylesheet is still empty. + const check250ms = async () => { + expect( + await contentDocument!.evaluate( + () => + document.adoptedStyleSheets.length === 1 && + document.adoptedStyleSheets[0].cssRules.length === 0, + ), + ).toBeTruthy(); + expect( + await contentDocument!.evaluate( + () => + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets.length === 1 && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules.length === 0, + ), + ).toBeTruthy(); + }; + + // At 300ms, the adopted stylesheet is replaced with new content. + const check300ms = async () => { + expect( + await contentDocument!.evaluate( + () => + document.adoptedStyleSheets[0].cssRules.length === 1 && + document.adoptedStyleSheets[0].cssRules[0].cssText === + 'div { color: yellow; }', + ), + ).toBeTruthy(); + expect( + await contentDocument!.evaluate( + () => + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules.length === 1 && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules[0].cssText === + 'h1 { color: blue; }', + ), + ).toBeTruthy(); + }; + + // At 400ms, check replaceSync API. + const check400ms = async () => { + expect( + await contentDocument!.evaluate( + () => + document.adoptedStyleSheets[0].cssRules.length === 1 && + document.adoptedStyleSheets[0].cssRules[0].cssText === + 'div { display: inline; }', + ), + ).toBeTruthy(); + expect( + await contentDocument!.evaluate( + () => + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules.length === 1 && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules[0].cssText === + 'h1 { font-size: large; }', + ), + ).toBeTruthy(); + }; + + // At 500ms, check CSSStyleDeclaration API. + const check500ms = async () => { + expect( + await contentDocument!.evaluate( + () => + document.adoptedStyleSheets[0].cssRules.length === 1 && + document.adoptedStyleSheets[0].cssRules[0].cssText === + 'div { color: green; }', + ), + ).toBeTruthy(); + expect( + await contentDocument!.evaluate( + () => + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules.length === 2 && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules[0].cssText === + 'h2 { color: red; }' && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules[1].cssText === + 'h1 { font-size: medium !important; }', + ), + ).toBeTruthy(); + }; + + // At 600ms, check insertRule and deleteRule API. + const check600ms = async () => { + expect( + await contentDocument!.evaluate( + () => + document.adoptedStyleSheets[0].cssRules.length === 2 && + document.adoptedStyleSheets[0].cssRules[0].cssText === + 'div { color: green; }' && + document.adoptedStyleSheets[0].cssRules[1].cssText === + 'body { border: 2px solid blue; }', + ), + ).toBeTruthy(); + expect( + await contentDocument!.evaluate( + () => + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules.length === 1 && + document.querySelector('iframe')!.contentDocument! + .adoptedStyleSheets[0].cssRules[0].cssText === + 'h1 { font-size: medium !important; }', + ), + ).toBeTruthy(); + }; + + await page.waitForTimeout(235); + await check250ms(); + + await page.waitForTimeout(50); + await check300ms(); + + await page.waitForTimeout(100); + await check400ms(); + + await page.waitForTimeout(100); + await check500ms(); + + await page.waitForTimeout(100); + await check600ms(); + + // To test the correctness of replaying adopted stylesheet mutation events in the fast-forward mode. + await page.evaluate('replayer.play(0);'); + await waitForRAF(page); + await page.evaluate('replayer.pause(280);'); + await check250ms(); + + await page.evaluate('replayer.pause(330);'); + await check300ms(); + + await page.evaluate('replayer.pause(430);'); + await check400ms(); + + await page.evaluate('replayer.pause(530);'); + await check500ms(); + + await page.evaluate('replayer.pause(630);'); + await check600ms(); + }); }); diff --git a/packages/rrweb/test/util.test.ts b/packages/rrweb/test/util.test.ts new file mode 100644 index 0000000000..964005c8c6 --- /dev/null +++ b/packages/rrweb/test/util.test.ts @@ -0,0 +1,78 @@ +/** + * @jest-environment jsdom + */ +import { StyleSheetMirror } from '../src/utils'; + +describe('Utilities for other modules', () => { + describe('StyleSheetMirror', () => { + it('should create a StyleSheetMirror', () => { + const mirror = new StyleSheetMirror(); + expect(mirror).toBeDefined(); + expect(mirror.add).toBeDefined(); + expect(mirror.has).toBeDefined(); + expect(mirror.reset).toBeDefined(); + expect(mirror.getId).toBeDefined(); + }); + + it('can add CSSStyleSheet into the mirror without ID parameter', () => { + const mirror = new StyleSheetMirror(); + const styleSheet = new CSSStyleSheet(); + expect(mirror.has(styleSheet)).toBeFalsy(); + expect(mirror.add(styleSheet)).toEqual(1); + expect(mirror.has(styleSheet)).toBeTruthy(); + // This stylesheet has been added before so just return its assigned id. + expect(mirror.add(styleSheet)).toEqual(1); + + for (let i = 0; i < 10; i++) { + const styleSheet = new CSSStyleSheet(); + expect(mirror.has(styleSheet)).toBeFalsy(); + expect(mirror.add(styleSheet)).toEqual(i + 2); + expect(mirror.has(styleSheet)).toBeTruthy(); + } + }); + + it('can add CSSStyleSheet into the mirror with ID parameter', () => { + const mirror = new StyleSheetMirror(); + for (let i = 0; i < 10; i++) { + const styleSheet = new CSSStyleSheet(); + expect(mirror.has(styleSheet)).toBeFalsy(); + expect(mirror.add(styleSheet, i)).toEqual(i); + expect(mirror.has(styleSheet)).toBeTruthy(); + } + }); + + it('can get the id from the mirror', () => { + const mirror = new StyleSheetMirror(); + for (let i = 0; i < 10; i++) { + const styleSheet = new CSSStyleSheet(); + mirror.add(styleSheet); + expect(mirror.getId(styleSheet)).toBe(i + 1); + } + expect(mirror.getId(new CSSStyleSheet())).toBe(-1); + }); + + it('can get CSSStyleSheet objects with id', () => { + const mirror = new StyleSheetMirror(); + for (let i = 0; i < 10; i++) { + const styleSheet = new CSSStyleSheet(); + mirror.add(styleSheet); + expect(mirror.getStyle(i + 1)).toBe(styleSheet); + } + }); + + it('can reset the mirror', () => { + const mirror = new StyleSheetMirror(); + const styleList: CSSStyleSheet[] = []; + for (let i = 0; i < 10; i++) { + const styleSheet = new CSSStyleSheet(); + mirror.add(styleSheet); + expect(mirror.getId(styleSheet)).toBe(i + 1); + styleList.push(styleSheet); + } + expect(mirror.reset()).toBeUndefined(); + for (let s of styleList) expect(mirror.has(s)).toBeFalsy(); + for (let i = 0; i < 10; i++) expect(mirror.getStyle(i + 1)).toBeNull(); + expect(mirror.add(new CSSStyleSheet())).toBe(1); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index a887541e33..b2f2d2feaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1618,6 +1618,16 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@mdn/browser-compat-data@^3.3.14": + version "3.3.14" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-3.3.14.tgz#b72a37c654e598f9ae6f8335faaee182bebc6b28" + integrity sha512-n2RC9d6XatVbWFdHLimzzUJxJ1KY8LdjqrW6YvGPiRmsHkhOUx74/Ct10x5Yo7bC/Jvqx7cDEW8IMPv/+vwEzA== + +"@mdn/browser-compat-data@^4.1.5": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz#1fead437f3957ceebe2e8c3f46beccdb9bc575b8" + integrity sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA== + "@microsoft/tsdoc-config@0.16.1": version "0.16.1" resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz#4de11976c1202854c4618f364bf499b4be33e657" @@ -2766,6 +2776,13 @@ 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= +ast-metadata-inferer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/ast-metadata-inferer/-/ast-metadata-inferer-0.7.0.tgz#c45d874cbdecabea26dc5de11fc6fa1919807c66" + integrity sha512-OkMLzd8xelb3gmnp6ToFvvsHLtS6CbagTkFQvQ+ZYFe3/AIl9iKikNR9G7pY3GfOR/2Xc222hwBjzI7HLkE76Q== + dependencies: + "@mdn/browser-compat-data" "^3.3.14" + async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" @@ -3052,6 +3069,16 @@ browserslist@^4.16.6: node-releases "^1.1.77" picocolors "^0.2.1" +browserslist@^4.16.8, browserslist@^4.21.4: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + dependencies: + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" + browserslist@^4.17.5: version "4.19.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz" @@ -3217,20 +3244,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.0, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001304, caniuse-lite@^1.0.30001400: + version "1.0.30001402" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz" + integrity sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew== caseless@~0.12.0: version "0.12.0" @@ -3607,6 +3624,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +construct-style-sheets-polyfill@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.1.0.tgz#c490abd79efdb359fafa62ec14ea55232be0eecf" + integrity sha512-HBLKP0chz8BAY6rBdzda11c3wAZeCZ+kIG4weVC2NM3AXzxx09nhe8t0SQNdloAvg5GLuHwq/0SPOOSPvtCcKw== + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" @@ -3724,6 +3746,11 @@ core-js@^2.4.0: resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3.16.2: + version "3.25.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.1.tgz#5818e09de0db8956e16bf10e2a7141e931b7c69c" + integrity sha512-sr0FY4lnO1hkQ4gLDr24K0DGnweGO1QwSj5BpfQjpSJPdqWalja4cTps29Y/PJVG/P7FYlPDkH3hO+Tr0CvDgQ== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" @@ -4291,6 +4318,11 @@ electron-to-chromium@^1.4.17: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.37.tgz" integrity sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg== +electron-to-chromium@^1.4.251: + version "1.4.254" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.254.tgz#c6203583890abf88dfc0be046cd72d3b48f8beb6" + integrity sha512-Sh/7YsHqQYkA6ZHuHMy24e6TE4eX6KZVsZb9E/DvU1nQRIrH4BflO/4k+83tfdYvDl+MObvlqHPRICzEdC9c6Q== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" @@ -4614,6 +4646,20 @@ eslint-config-google@^0.14.0: resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== +eslint-plugin-compat@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-4.0.2.tgz#b058627a7d25d352adf0ec16dca8fcf92d9c7af7" + integrity sha512-xqvoO54CLTVaEYGMzhu35Wzwk/As7rCvz/2dqwnFiWi0OJccEtGIn+5qq3zqIu9nboXlpdBN579fZcItC73Ycg== + dependencies: + "@mdn/browser-compat-data" "^4.1.5" + ast-metadata-inferer "^0.7.0" + browserslist "^4.16.8" + caniuse-lite "^1.0.30001304" + core-js "^3.16.2" + find-up "^5.0.0" + lodash.memoize "4.1.2" + semver "7.3.5" + eslint-plugin-jest@^26.5.3: version "26.5.3" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" @@ -7677,7 +7723,7 @@ lodash.ismatch@^4.4.0: resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= -lodash.memoize@4.x, lodash.memoize@^4.1.2: +lodash.memoize@4.1.2, lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -8270,6 +8316,11 @@ node-releases@^2.0.1: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + nopt@^4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" @@ -10117,7 +10168,7 @@ semver-match@0.1.1: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -11301,6 +11352,14 @@ upath@^2.0.1: resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== +update-browserslist-db@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" + integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" From 55ebce73376535bc9710e3088abd25aced68b4a4 Mon Sep 17 00:00:00 2001 From: MF Date: Fri, 30 Sep 2022 16:14:35 +1000 Subject: [PATCH 070/201] Fix inline link elements bug (#995) * fix: bug when inlined link elements * test: update snapshot for test cases * apply Justin's review suggestions 1. make Mirror's replace function act the same with the original one when there's no existed node to get replaced. 2. when replacing with the link/style elements, keep their existing attributes to prevent potential bugs --- packages/rrdom/src/index.ts | 5 ++ packages/rrweb-snapshot/src/snapshot.ts | 1 - packages/rrweb/src/record/index.ts | 2 +- packages/rrweb/src/record/mutation.ts | 2 +- .../rrweb/src/record/stylesheet-manager.ts | 29 ++++---- packages/rrweb/src/replay/index.ts | 37 ++++++++++ .../test/__snapshots__/record.test.ts.snap | 74 ++++++++++++------- packages/rrweb/test/record.test.ts | 13 +++- 8 files changed, 116 insertions(+), 47 deletions(-) diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index 776427a11a..da96b12db2 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -389,6 +389,11 @@ export class Mirror implements IMirror { } replace(id: number, n: RRNode) { + const oldNode = this.getNode(id); + if (oldNode) { + const meta = this.nodeMetaMap.get(oldNode); + if (meta) this.nodeMetaMap.set(n, meta); + } this.idNodeMap.set(id, n); } diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 1b42dd5b68..3708001599 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -1165,7 +1165,6 @@ export function serializeNodeWithId( }, stylesheetLoadTimeout, ); - if (isStylesheetLoaded(n as HTMLLinkElement) === false) return null; // add stylesheet in later mutation } return serializedNode; diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 1718a036f4..26e07fc83a 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -329,7 +329,7 @@ function record( shadowDomManager.observeAttachShadow(iframe); }, onStylesheetLoad: (linkEl, childSn) => { - stylesheetManager.attachLinkElement(linkEl, childSn, mirror); + stylesheetManager.attachLinkElement(linkEl, childSn); }, keepIframeSrcFn, }); diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index c852f3eb27..d7724a16ba 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -324,7 +324,7 @@ export default class MutationBuffer { this.shadowDomManager.observeAttachShadow(iframe); }, onStylesheetLoad: (link, childSn) => { - this.stylesheetManager.attachLinkElement(link, childSn, this.mirror); + this.stylesheetManager.attachLinkElement(link, childSn); }, }); if (sn) { diff --git a/packages/rrweb/src/record/stylesheet-manager.ts b/packages/rrweb/src/record/stylesheet-manager.ts index 1a2f6dc1a5..35d8c513db 100644 --- a/packages/rrweb/src/record/stylesheet-manager.ts +++ b/packages/rrweb/src/record/stylesheet-manager.ts @@ -1,8 +1,9 @@ -import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; +import type { elementNode, serializedNodeWithId } from 'rrweb-snapshot'; import { getCssRuleString } from 'rrweb-snapshot'; import type { adoptedStyleSheetCallback, adoptedStyleSheetParam, + attributeMutation, mutationCallBack, } from '../types'; import { StyleSheetMirror } from '../utils'; @@ -24,20 +25,20 @@ export class StylesheetManager { public attachLinkElement( linkEl: HTMLLinkElement, childSn: serializedNodeWithId, - mirror: Mirror, ) { - this.mutationCb({ - adds: [ - { - parentId: mirror.getId(linkEl), - nextId: null, - node: childSn, - }, - ], - removes: [], - texts: [], - attributes: [], - }); + if ('_cssText' in (childSn as elementNode).attributes) + this.mutationCb({ + adds: [], + removes: [], + texts: [], + attributes: [ + { + id: childSn.id, + attributes: (childSn as elementNode) + .attributes as attributeMutation['attributes'], + }, + ], + }); this.trackLinkElement(linkEl); } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index ae33316b2d..fd9fbf4b19 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -6,6 +6,8 @@ import { createCache, Mirror, createMirror, + attributes, + serializedElementNodeWithId, } from 'rrweb-snapshot'; import { RRDocument, @@ -1643,6 +1645,41 @@ export class Replayer { (target as Element | 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 Node & RRNode, + ) as serializedElementNodeWithId; + Object.assign( + newSn.attributes, + mutation.attributes as attributes, + ); + const newNode = buildNodeWithSN(newSn, { + doc: target.ownerDocument as Document, // can be Document or RRDocument + mirror: mirror as Mirror, + skipChild: true, + hackCss: true, + cache: this.cache, + }); + const siblingNode = target.nextSibling; + const parentNode = target.parentNode; + if (newNode && parentNode) { + parentNode.removeChild(target as Node & RRNode); + parentNode.insertBefore( + newNode as Node & RRNode, + siblingNode as (Node & RRNode) | null, + ); + mirror.replace(mutation.id, newNode as Node & RRNode); + break; + } + } catch (e) { + // for safe + } + } (target as Element | RRElement).setAttribute( attributeName, value, diff --git a/packages/rrweb/test/__snapshots__/record.test.ts.snap b/packages/rrweb/test/__snapshots__/record.test.ts.snap index 4081bbeb5d..7133805635 100644 --- a/packages/rrweb/test/__snapshots__/record.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/record.test.ts.snap @@ -173,9 +173,12 @@ exports[`record captures CORS stylesheets that are still loading 1`] = ` \\"type\\": 3, \\"data\\": { \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], \\"adds\\": [ { - \\"parentId\\": 9, + \\"parentId\\": 4, \\"nextId\\": null, \\"node\\": { \\"type\\": 2, @@ -188,10 +191,7 @@ exports[`record captures CORS stylesheets that are still loading 1`] = ` \\"id\\": 9 } } - ], - \\"removes\\": [], - \\"texts\\": [], - \\"attributes\\": [] + ] } } ]" @@ -2007,7 +2007,19 @@ exports[`record captures stylesheets in iframes that are still loading 1`] = ` \\"type\\": 2, \\"tagName\\": \\"head\\", \\"attributes\\": {}, - \\"childNodes\\": [], + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"link\\", + \\"attributes\\": { + \\"rel\\": \\"stylesheet\\", + \\"href\\": \\"blob:null\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 10, + \\"id\\": 13 + } + ], \\"rootId\\": 10, \\"id\\": 12 }, @@ -2039,25 +2051,17 @@ exports[`record captures stylesheets in iframes that are still loading 1`] = ` \\"type\\": 3, \\"data\\": { \\"source\\": 0, - \\"adds\\": [ + \\"adds\\": [], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [ { - \\"parentId\\": 13, - \\"nextId\\": null, - \\"node\\": { - \\"type\\": 2, - \\"tagName\\": \\"link\\", - \\"attributes\\": { - \\"_cssText\\": \\"body { color: pink; }\\" - }, - \\"childNodes\\": [], - \\"rootId\\": 10, - \\"id\\": 13 + \\"id\\": 13, + \\"attributes\\": { + \\"_cssText\\": \\"body { color: pink; }\\" } } - ], - \\"removes\\": [], - \\"texts\\": [], - \\"attributes\\": [] + ] } } ]" @@ -2286,24 +2290,42 @@ exports[`record captures stylesheets that are still loading 1`] = ` \\"type\\": 3, \\"data\\": { \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], \\"adds\\": [ { - \\"parentId\\": 9, + \\"parentId\\": 4, \\"nextId\\": null, \\"node\\": { \\"type\\": 2, \\"tagName\\": \\"link\\", \\"attributes\\": { - \\"_cssText\\": \\"body { color: pink; }\\" + \\"rel\\": \\"stylesheet\\", + \\"href\\": \\"blob:null\\" }, \\"childNodes\\": [], \\"id\\": 9 } } - ], + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [], \\"removes\\": [], \\"texts\\": [], - \\"attributes\\": [] + \\"attributes\\": [ + { + \\"id\\": 9, + \\"attributes\\": { + \\"_cssText\\": \\"body { color: pink; }\\" + } + } + ] } } ]" diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index db7e0be4d4..e353559a26 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -626,8 +626,11 @@ describe('record', function (this: ISuite) { // `blob:` URLs are not available immediately, so we need to wait for the browser to load them await waitForRAF(ctx.page); - - assertSnapshot(ctx.events); + // 'blob' URL is different in every execution so we need to remove it from the snapshot. + const filteredEvents = JSON.parse( + JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'), + ); + assertSnapshot(filteredEvents); }); it('captures stylesheets in iframes that are still loading', async () => { @@ -659,8 +662,10 @@ describe('record', function (this: ISuite) { // `blob:` URLs are not available immediately, so we need to wait for the browser to load them await waitForRAF(ctx.page); - - assertSnapshot(ctx.events); + const filteredEvents = JSON.parse( + JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'), + ); + assertSnapshot(filteredEvents); }); it('captures CORS stylesheets that are still loading', async () => { From 6f44bb72a194ad428bc697229f589c17d713da2c Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Fri, 30 Sep 2022 08:15:24 +0200 Subject: [PATCH 071/201] Call `afterAppend` for mutations and top level elements (#1012) --- packages/rrweb-snapshot/src/rebuild.ts | 4 ++ packages/rrweb/src/replay/index.ts | 93 ++++++++++++++++---------- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 6907802da7..a491122d6b 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -348,6 +348,10 @@ export function buildNodeWithSN( mirror: Mirror; skipChild?: boolean; hackCss: boolean; + /** + * This callback will be called for each of this nodes' `.childNodes` after they are appended to _this_ node. + * Caveat: This callback _doesn't_ get called when this node is appended to the DOM. + */ afterAppend?: (n: Node, id: number) => unknown; cache: BuildCache; }, diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index fd9fbf4b19..6226211ed4 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -754,21 +754,25 @@ export class Replayer { } this.legacy_missingNodeRetryMap = {}; const collected: AppendedIframe[] = []; + const afterAppend = (builtNode: Node, id: number) => { + this.collectIframeAndAttachDocument(collected, builtNode); + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) + plugin.onBuild(builtNode, { + id, + replayer: this, + }); + } + }; + rebuild(event.data.node, { doc: this.iframe.contentDocument, - afterAppend: (builtNode: Node, id: number) => { - this.collectIframeAndAttachDocument(collected, builtNode); - for (const plugin of this.config.plugins || []) { - if (plugin.onBuild) - plugin.onBuild(builtNode, { - id, - replayer: this, - }); - } - }, + afterAppend, cache: this.cache, mirror: this.mirror, }); + afterAppend(this.iframe.contentDocument, event.data.node.id); + for (const { mutationInQueue, builtNode } of collected) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( @@ -840,35 +844,39 @@ export class Replayer { type TMirror = typeof mirror extends Mirror ? Mirror : RRDOMMirror; const collected: AppendedIframe[] = []; + const afterAppend = (builtNode: Node, id: number) => { + this.collectIframeAndAttachDocument(collected, builtNode); + const sn = (mirror as TMirror).getMeta((builtNode as unknown) as TNode); + if ( + sn?.type === NodeType.Element && + sn?.tagName.toUpperCase() === 'HTML' + ) { + const { documentElement, head } = iframeEl.contentDocument!; + this.insertStyleRules( + documentElement as HTMLElement | RRElement, + head as HTMLElement | RRElement, + ); + } + + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) + plugin.onBuild(builtNode, { + id, + replayer: this, + }); + } + }; + buildNodeWithSN(mutation.node, { doc: iframeEl.contentDocument! as Document, mirror: mirror as Mirror, hackCss: true, skipChild: false, - afterAppend: (builtNode, id: number) => { - this.collectIframeAndAttachDocument(collected, builtNode); - const sn = (mirror as TMirror).getMeta((builtNode as unknown) as TNode); - if ( - sn?.type === NodeType.Element && - sn?.tagName.toUpperCase() === 'HTML' - ) { - const { documentElement, head } = iframeEl.contentDocument!; - this.insertStyleRules( - documentElement as HTMLElement | RRElement, - head as HTMLElement | RRElement, - ); - } - - for (const plugin of this.config.plugins || []) { - if (plugin.onBuild) - plugin.onBuild(builtNode, { - id, - replayer: this, - }); - } - }, + afterAppend, cache: this.cache, }); + afterAppend(iframeEl.contentDocument! as Document, mutation.node.id); + for (const { mutationInQueue, builtNode } of collected) { this.attachDocumentToIframe(mutationInQueue, builtNode); this.newDocumentQueue = this.newDocumentQueue.filter( @@ -1466,17 +1474,23 @@ export class Replayer { ); return; } + const afterAppend = (node: Node | RRNode, id: number) => { + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) plugin.onBuild(node, { id, replayer: this }); + } + }; + const target = buildNodeWithSN(mutation.node, { doc: targetDoc as Document, // can be Document or RRDocument mirror: mirror as Mirror, // can be this.mirror or virtualDom.mirror skipChild: true, hackCss: true, cache: this.cache, - afterAppend: (node: Node | RRNode, id: number) => { - for (const plugin of this.config.plugins || []) { - if (plugin.onBuild) plugin.onBuild(node, { id, replayer: this }); - } - }, + /** + * 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 Node | RRNode; // legacy data, we should not have -1 siblings any more @@ -1536,6 +1550,11 @@ export class Replayer { (parent as TNode).appendChild(target as TNode); } + /** + * 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. From e86d482ffd2f7b58ae1d6b67702ad5dc32bed0b5 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Fri, 30 Sep 2022 14:16:57 +0800 Subject: [PATCH 072/201] chore(release): publish new version - rrdom-nodejs@0.1.6 - rrdom@0.1.6 - rrweb-player@1.0.0-alpha.3 - rrweb-snapshot@2.0.0-alpha.3 - rrweb@2.0.0-alpha.3 --- packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/package.json | 4 ++-- packages/rrweb-player/package.json | 4 ++-- packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/package.json | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 4a14308b88..84bcc4578c 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "0.1.5", + "version": "0.1.6", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,8 +49,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^0.1.5", - "rrweb-snapshot": "^2.0.0-alpha.2" + "rrdom": "^0.1.6", + "rrweb-snapshot": "^2.0.0-alpha.3" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 3b755cca81..74ef10f11b 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "0.1.5", + "version": "0.1.6", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", @@ -46,6 +46,6 @@ "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.2" + "rrweb-snapshot": "^2.0.0-alpha.3" } } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 04ff6b1f4a..9489effec1 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-player", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", @@ -24,7 +24,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.2" + "rrweb": "^2.0.0-alpha.3" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 13e133e44c..9cc839218b 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index d4cfcff22f..efc065233d 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -85,7 +85,7 @@ "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^0.1.5", - "rrweb-snapshot": "^2.0.0-alpha.2" + "rrdom": "^0.1.6", + "rrweb-snapshot": "^2.0.0-alpha.3" } } From 9de0de2cc56262234d1167d880981f1e99aa28a9 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 4 Oct 2022 08:27:53 +0200 Subject: [PATCH 073/201] Add maxScale to rrwebPlayer types and add docs to others (#1014) --- packages/rrweb-player/typings/index.d.ts | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/rrweb-player/typings/index.d.ts b/packages/rrweb-player/typings/index.d.ts index 7412448170..6d15307bde 100644 --- a/packages/rrweb-player/typings/index.d.ts +++ b/packages/rrweb-player/typings/index.d.ts @@ -5,13 +5,50 @@ import { SvelteComponent } from 'svelte'; export type RRwebPlayerOptions = { target: HTMLElement; props: { + /** + * The events to replay. + * @default `[]` + */ events: eventWithTime[]; + /** + * The width of the replayer + * @defaultValue `1024` + */ width?: number; + /** + * The height of the replayer + * @defaultValue `576` + */ height?: number; + /** + * The maximum scale of the replayer (1 = 100%). Set to 0 for unlimited + * @defaultValue `1` + */ + maxScale?: number; + /** + * Whether to autoplay + * @defaultValue `true` + */ autoPlay?: boolean; + /** + * The default speed to play at + * @defaultValue `1` + */ speed?: number; + /** + * Speed options in UI + * @defaultValue `[1, 2, 4, 8]` + */ speedOption?: number[]; + /** + * Whether to show the controller UI + * @defaultValue `true` + */ showController?: boolean; + /** + * Customize the custom events style with a key-value map + * @defaultValue `{}` + */ tags?: Record; } & Partial; }; From 20ad416d9b4d2376ec4d090a39d5088ac65c37d6 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 5 Oct 2022 04:52:19 +0100 Subject: [PATCH 074/201] =?UTF-8?q?`prototype`=20here=20does=20not=20refer?= =?UTF-8?q?=20to=20the=20correct=20WebGLRenderingContext=20=E2=80=A6=20(#1?= =?UTF-8?q?013)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * `prototype` here does not refer to the correct WebGLRenderingContext when there are multiple being patched - all were referring to the same one meaning that variables weren't being indexed correctly as `canvasVarMap` in serialize-args.ts was only seeing a single context If you revert the change from `prototype` to `this` from this commit, you'll get the following failure in the new test added in this commit: @@ -199,11 +199,11 @@ "property": "bindBuffer", "args": [ 34962, { "rr_type": "WebGLBuffer", - "index": 0 + "index": 1 } ] }, This is because the 'confound' canvas was populating a single entry in the canvasVarMap so when 'myCanvas' starts populating, there is already an entry (which doesn't exist on the replay side as the 'confound' canvas was never emitted) * Update packages/rrweb/test/integration.test.ts Co-authored-by: Justin Halsall * Update packages/rrweb/test/html/canvas-webgl-shader.html Co-authored-by: Justin Halsall Co-authored-by: Justin Halsall --- .../src/record/observers/canvas/webgl.ts | 4 +- .../__snapshots__/integration.test.ts.snap | 414 ++++++++++++++++++ .../rrweb/test/html/canvas-webgl-shader.html | 61 +++ packages/rrweb/test/integration.test.ts | 13 + 4 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 packages/rrweb/test/html/canvas-webgl-shader.html diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index 9c6a3b7cbd..f3397d76a9 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -36,9 +36,9 @@ function patchGLPrototype( ) { return function (this: typeof prototype, ...args: Array) { const result = original.apply(this, args); - saveWebGLVar(result, win, prototype); + saveWebGLVar(result, win, this); if (!isBlocked(this.canvas, blockClass, blockSelector, true)) { - const recordArgs = serializeArgs([...args], win, prototype); + const recordArgs = serializeArgs([...args], win, this); const mutation: canvasMutationWithType = { type, property: prop, diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 6177bb10bf..61c6d0b465 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1,5 +1,419 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`record integration tests can correctly serialize a shader and multiple webgl contexts 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"canvas shader\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": { + \\"id\\": \\"myCanvas\\", + \\"width\\": \\"300\\", + \\"height\\": \\"300\\", + \\"style\\": \\"border: 1px solid #000000\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 20 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 23 + } + ], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 24 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 9, + \\"id\\": 16, + \\"type\\": 1, + \\"commands\\": [ + { + \\"property\\": \\"createBuffer\\", + \\"args\\": [] + }, + { + \\"property\\": \\"bindBuffer\\", + \\"args\\": [ + 34962, + { + \\"rr_type\\": \\"WebGLBuffer\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"bufferData\\", + \\"args\\": [ + 34962, + { + \\"rr_type\\": \\"Float32Array\\", + \\"args\\": [ + [ + -0.5, + 0.5, + -0.5, + -0.5, + 0, + -0.5 + ] + ] + }, + 35044 + ] + }, + { + \\"property\\": \\"bindBuffer\\", + \\"args\\": [ + 34962, + null + ] + }, + { + \\"property\\": \\"createShader\\", + \\"args\\": [ + 35633 + ] + }, + { + \\"property\\": \\"shaderSource\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 0 + }, + \\"attribute vec2 coordinates;void main(void) { gl_Position = vec4(coordinates,0.0, 1.0);}\\" + ] + }, + { + \\"property\\": \\"compileShader\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"createShader\\", + \\"args\\": [ + 35632 + ] + }, + { + \\"property\\": \\"shaderSource\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 1 + }, + \\"void main(void) {gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);}\\" + ] + }, + { + \\"property\\": \\"compileShader\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 1 + } + ] + }, + { + \\"property\\": \\"createProgram\\", + \\"args\\": [] + }, + { + \\"property\\": \\"attachShader\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + }, + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"attachShader\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + }, + { + \\"rr_type\\": \\"WebGLShader\\", + \\"index\\": 1 + } + ] + }, + { + \\"property\\": \\"linkProgram\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"useProgram\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"bindBuffer\\", + \\"args\\": [ + 34962, + { + \\"rr_type\\": \\"WebGLBuffer\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"getAttribLocation\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + }, + \\"coordinates\\" + ] + }, + { + \\"property\\": \\"vertexAttribPointer\\", + \\"args\\": [ + 0, + 2, + 5126, + false, + 0, + 0 + ] + }, + { + \\"property\\": \\"enableVertexAttribArray\\", + \\"args\\": [ + 0 + ] + }, + { + \\"property\\": \\"clearColor\\", + \\"args\\": [ + 0.5, + 0.5, + 0.5, + 0.9 + ] + }, + { + \\"property\\": \\"enable\\", + \\"args\\": [ + 2929 + ] + }, + { + \\"property\\": \\"clear\\", + \\"args\\": [ + 16384 + ] + }, + { + \\"property\\": \\"viewport\\", + \\"args\\": [ + 0, + 0, + 300, + 300 + ] + }, + { + \\"property\\": \\"drawArrays\\", + \\"args\\": [ + 4, + 0, + 3 + ] + } + ] + } + } +]" +`; + exports[`record integration tests can freeze mutations 1`] = ` "[ { diff --git a/packages/rrweb/test/html/canvas-webgl-shader.html b/packages/rrweb/test/html/canvas-webgl-shader.html new file mode 100644 index 0000000000..f110a2fdec --- /dev/null +++ b/packages/rrweb/test/html/canvas-webgl-shader.html @@ -0,0 +1,61 @@ + + + + + + canvas shader + + + + + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index a9fab6a2ca..8b5524bbde 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -401,6 +401,19 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('can correctly serialize a shader and multiple webgl contexts', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml.call(this, 'canvas-webgl-shader.html', { + recordCanvas: true, + }), + ); + await waitForRAF(page); + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('will serialize node before record', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From 87aa3b6a3d4368d8a3e3cf9f25cb07420bdd05b5 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Sat, 8 Oct 2022 03:28:01 +0100 Subject: [PATCH 075/201] Ignore noisy webgl params (#1016) * `isContextLost` is particularly noisy, being called almost constantly by e.g. Figma to check whether there's a need to re initialize a canvas Juice10: We could perhaps look at https://github.com/microsoft/TypeScript/blob/5cd49f6cbcd2effe9d425dee3a39cb49209bb656/lib/lib.dom.d.ts#L14578 via https://stackoverflow.com/questions/60150251/how-to-import-an-interface-from-lib-dom-d-ts * Apply formatting changes Co-authored-by: eoghanmurray --- packages/rrweb/src/record/observers/canvas/webgl.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/rrweb/src/record/observers/canvas/webgl.ts b/packages/rrweb/src/record/observers/canvas/webgl.ts index f3397d76a9..9e8bfbf468 100644 --- a/packages/rrweb/src/record/observers/canvas/webgl.ts +++ b/packages/rrweb/src/record/observers/canvas/webgl.ts @@ -24,6 +24,18 @@ function patchGLPrototype( const props = Object.getOwnPropertyNames(prototype); for (const prop of props) { + if ( + //prop.startsWith('get') || // e.g. getProgramParameter, but too risky + [ + 'isContextLost', + 'canvas', + 'drawingBufferWidth', + 'drawingBufferHeight', + ].includes(prop) + ) { + // skip read only propery/functions + continue; + } try { if (typeof prototype[prop as keyof typeof prototype] !== 'function') { continue; From 7edfefe680d6710f653f458e555ec8b9d9140566 Mon Sep 17 00:00:00 2001 From: MF Date: Wed, 12 Oct 2022 23:51:23 +1100 Subject: [PATCH 076/201] fix: recording bug in youtube and bitbucket (#1020) when getting shadow host elements, the anchor element also has a host property as a string. This unexpected value can crash recorder --- packages/rrweb/src/record/mutation.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index d7724a16ba..5f54842b95 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -266,15 +266,20 @@ export default class MutationBuffer { return nextId; }; const pushAdd = (n: Node) => { - const shadowHost: Element | null = n.getRootNode - ? (n.getRootNode() as ShadowRoot)?.host - : null; + let shadowHost: Element | null = null; + if ( + n.getRootNode?.()?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && + (n.getRootNode() as ShadowRoot).host + ) + shadowHost = (n.getRootNode() as ShadowRoot).host; // If n is in a nested shadow dom. let rootShadowHost = shadowHost; - while ((rootShadowHost?.getRootNode?.() as ShadowRoot | undefined)?.host) - rootShadowHost = - (rootShadowHost?.getRootNode?.() as ShadowRoot | undefined)?.host || - null; + while ( + rootShadowHost?.getRootNode?.()?.nodeType === + Node.DOCUMENT_FRAGMENT_NODE && + (rootShadowHost.getRootNode() as ShadowRoot).host + ) + rootShadowHost = (rootShadowHost.getRootNode() as ShadowRoot).host; // ensure contains is passed a Node, or it will throw an error const notInDoc = !this.doc.contains(n) && From 1ec4413137d906664120516b1769b5cfb2552fa8 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Sun, 16 Oct 2022 07:41:04 +0200 Subject: [PATCH 077/201] doc: improve contributor experience (#1024) --- CONTRIBUTING.md | 12 ++++++++++++ packages/rrweb/scripts/repl.js | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7a0df1668..d5b2014ae5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,18 @@ We actively welcome your pull requests. We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. +## Run locally + +- Install dependencies: `yarn` +- Run recorder on a website: `yarn repl` +- Run a cobrowsing/mirroring session locally: `yarn live-stream` +- Test: `yarn test` or `yarn test:watch` +- Lint: `yarn lint` + +## Coding style + +See [documentation](docs/development/coding-style.md) + ## License rrweb is [MIT licensed](https://github.com/rrweb-io/rrweb/blob/master/LICENSE). diff --git a/packages/rrweb/scripts/repl.js b/packages/rrweb/scripts/repl.js index 5319bcf9f9..4edb07a3c3 100644 --- a/packages/rrweb/scripts/repl.js +++ b/packages/rrweb/scripts/repl.js @@ -21,7 +21,7 @@ void (async () => { const code = getCode(); let events = []; - await start(); + await start('https://react-redux.realworld.io'); const fakeGoto = async (page, url) => { const intercept = async (request) => { @@ -38,17 +38,21 @@ void (async () => { page.off('request', intercept); }; - async function start() { + async function start(defaultURL) { events = []; - const { url } = await inquirer.prompt([ + let { url } = await inquirer.prompt([ { type: 'input', name: 'url', message: - 'Enter the url you want to record, e.g https://react-redux.realworld.io: ', + `Enter the url you want to record, e.g [${defaultURL}]: `, }, ]); + if (url === '') { + url = defaultURL; + } + console.log(`Going to open ${url}...`); await record(url); console.log('Ready to record. You can do any interaction on the page.'); @@ -92,7 +96,7 @@ void (async () => { ]); if (shouldRecordAnother) { - start(); + start(url); } else { process.exit(); } @@ -207,9 +211,9 @@ void (async () => { +``` + +Or installed by using NPM: + +```shell +npm install --save rrweb-player +``` -This is a project template for [Svelte](https://svelte.technology) apps. It lives at https://github.com/sveltejs/template. +```js +import rrwebPlayer from 'rrweb-player'; +import 'rrweb-player/dist/style.css'; +``` -To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): +## Usage -```bash -npm install -g degit # you only need to do this once +```js +new rrwebPlayer({ + target: document.body, // customizable root element + props: { + events, + }, +}); +``` -degit sveltejs/template svelte-app -cd svelte-app +## Options + +| key | default | description | +| -------------- | ------------ | ------------------------------------------------------------------------------------------------------------------- | +| events | [] | the events for replaying | +| width | 1024 | the width of the replayer | +| height | 576 | the height of the replayer | +| maxScale | 1 | the maximum scale of the replayer (1 = 100%), set to 0 for unlimited | +| autoPlay | true | whether to autoplay | +| speed | 1 | The default speed to play at | +| speedOption | [1, 2, 4, 8] | speed options in UI | +| showController | true | whether to show the controller UI | +| tags | {} | customize the custom events style with a key-value map | +| inactiveColor | #D4D4D4 | Customize the color of inactive periods indicator in the progress bar with a valid CSS color string. | +| ... | - | all the [rrweb Replayer options](https://github.com/rrweb-io/rrweb/blob/master/guide.md#options-1) will be bypassed | + +## methods on the rrwebPlayer component + +```ts +addEventListener(event: string, handler: (params: any) => unknown): void; ``` -_Note that you will need to have [Node.js](https://nodejs.org) installed._ +```ts +addEvent(event: eventWithTime): void; +``` -## Get started +```ts +getMetaData() => { + startTime: number; + endTime: number; + totalTime: number; +} +``` -Install the dependencies... +```ts +getReplayer() => Replayer; +``` -```bash -cd svelte-app -npm install +```ts +getMirror() => Mirror; ``` -...then start [Rollup](https://rollupjs.org): +Toggles between play/pause -```bash -npm run dev +```ts +toggle(); ``` -Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. +Sets speed of player -## Deploying to the web +```ts +setSpeed(speed: number) +``` -### With [now](https://zeit.co/now) +Turns on/off skip inactive -Install `now` if you haven't already: +```ts +toggleSkipInactive(); +``` -```bash -npm install -g now +Triggers resize, do this whenever you change width/height + +```ts +triggerResize(); ``` -Then, from within your project folder: +Plays replay -```bash -now +```ts +play(); ``` -As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon. +Pauses replay -### With [surge](https://surge.sh/) +```ts +pause(); +``` -Install `surge` if you haven't already: +Go to a point in time and pause or play from then -```bash -npm install -g surge +```ts +goto(timeOffset: number, play?: boolean) ``` -Then, from within your project folder: +Plays from a time to a time and (optionally) loop -```bash -npm run build -surge public +```ts +playRange( + timeOffset: number, + endTimeOffset: number, + startLooping: boolean = false, + afterHook: undefined | (() => void) = undefined, + ) ``` From 9bbc3e007334dc9ba712fd41cb4ffc0a73b54949 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sat, 12 Nov 2022 09:02:30 +0100 Subject: [PATCH 087/201] Fix: Capture css `background-clip: text` with browser prefix (#1047) --- packages/rrweb-snapshot/src/utils.ts | 30 ++++++++++++++++- .../__snapshots__/integration.test.ts.snap | 33 +++++++++++++++++++ .../test/html/background-clip-text.html | 33 +++++++++++++++++++ .../rrweb-snapshot/test/integration.test.ts | 17 ++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/rrweb-snapshot/test/html/background-clip-text.html diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 8f63e44f08..75e9a18008 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -24,10 +24,38 @@ export function isNativeShadowDom(shadowRoot: ShadowRoot) { return Object.prototype.toString.call(shadowRoot) === '[object ShadowRoot]'; } +/** + * Browsers sometimes destructively modify the css rules they receive. + * This function tries to rectify the modifications the browser made to make it more cross platform compatible. + * @param cssText - output of `CSSStyleRule.cssText` + * @returns `cssText` with browser inconsistencies fixed. + */ +function fixBrowserCompatibilityIssuesInCSS(cssText: string): string { + /** + * Chrome outputs `-webkit-background-clip` as `background-clip` in `CSSStyleRule.cssText`. + * But then Chrome ignores `background-clip` as css input. + * Re-introduce `-webkit-background-clip` to fix this issue. + */ + if ( + cssText.includes(' background-clip: text;') && + !cssText.includes(' -webkit-background-clip: text;') + ) { + cssText = cssText.replace( + ' background-clip: text;', + ' -webkit-background-clip: text; background-clip: text;', + ); + } + return cssText; +} + export function getCssRulesString(s: CSSStyleSheet): string | null { try { const rules = s.rules || s.cssRules; - return rules ? Array.from(rules).map(getCssRuleString).join('') : null; + return rules + ? fixBrowserCompatibilityIssuesInCSS( + Array.from(rules).map(getCssRuleString).join(''), + ) + : null; } catch (error) { return null; } diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 0706eba11c..29acb2b993 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -149,6 +149,20 @@ exports[`integration tests [html file]: about-mozilla.html 1`] = `

" `; +exports[`integration tests [html file]: background-clip-text.html 1`] = ` +" + + + + Document + + + +

The background is clipped to the foreground text.

+ + " +`; + exports[`integration tests [html file]: basic.html 1`] = ` " @@ -330,6 +344,25 @@ exports[`integration tests [html file]: picture.html 1`] = ` " `; +exports[`integration tests [html file]: picture-blob.html 1`] = ` +" + \\"This + + " +`; + +exports[`integration tests [html file]: picture-blob-in-frame.html 1`] = ` +" + + " +`; + +exports[`integration tests [html file]: picture-in-frame.html 1`] = ` +" + + " +`; + exports[`integration tests [html file]: preload.html 1`] = ` " diff --git a/packages/rrweb-snapshot/test/html/background-clip-text.html b/packages/rrweb-snapshot/test/html/background-clip-text.html new file mode 100644 index 0000000000..e4f1991a53 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/background-clip-text.html @@ -0,0 +1,33 @@ + + + + + + + Document + + + +

The background is clipped to the foreground text.

+ + + diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index 6ee32f9426..a9e0a34d68 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -276,6 +276,23 @@ iframe.contentDocument.querySelector('center').clientHeight assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); + + it('should save background-clip: text; as the more compatible -webkit-background-clip: test;', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`http://localhost:3030/html/background-clip-text.html`, { + waitUntil: 'load', + }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + window.snapshot = rrweb.snapshot(document, { + inlineStylesheet: true, + })`); + await page.waitFor(100); + const snapshot = (await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + )) as string; + assert(snapshot.includes('-webkit-background-clip: text;')); + }); }); describe('iframe integration tests', function (this: ISuite) { From fdb7135fbeddc4b4ddcb4711f028a1a3a2852ca5 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sat, 12 Nov 2022 09:03:30 +0100 Subject: [PATCH 088/201] Fix: scrolling on elements being is ignored (#1029) In certain cases when scrollLeft is being set, but the value doesn't change. Then scrollTop is also ignored, even if that value was changed. In this case we use the `scrollTo` api as it doesn't have that issue. --- packages/rrweb/src/replay/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 61696fbc56..201bd33919 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1757,8 +1757,11 @@ export class Replayer { }); } else { try { - (target as Element).scrollTop = d.y; - (target as Element).scrollLeft = d.x; + (target as Element).scrollTo({ + top: d.y, + left: d.x, + behavior: isSync ? 'auto' : 'smooth', + }); } catch (error) { /** * Seldomly we may found scroll target was removed before From a9fffb3bc0098cfa00253b1da1df84fc059dda1a Mon Sep 17 00:00:00 2001 From: luocongqiu Date: Sun, 13 Nov 2022 16:18:58 +0800 Subject: [PATCH 089/201] shadow root use the correct dom in iframe (#1037) Co-authored-by: luocq3 --- packages/rrweb/src/record/mutation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 461811303d..202efb24da 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -320,7 +320,7 @@ export default class MutationBuffer { ); } if (hasShadowRoot(n)) { - this.shadowDomManager.addShadowRoot(n.shadowRoot, document); + this.shadowDomManager.addShadowRoot(n.shadowRoot, this.doc); } }, onIframeLoad: (iframe, childSn) => { From 2a809499480ae4f7118432f09871c5f75fda06d7 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Wed, 16 Nov 2022 06:11:11 +0100 Subject: [PATCH 090/201] Cross origin iframe support (#1035) * Add `recordCrossOriginIframe` setting * Set up messaging between iframes * should emit full snapshot event from iframe as mutation event * this.mirror was dropped on attachIframe * should use unique id for child of iframe * Cross origin iframe recording in `yarn live-stream` * Root iframe check thats supported by firefox * Live stream: Inject script in all frames * Record same origin and cross origin iframes differently * Should map Input events correctly * Turn on other tests * Fix compatibility with newer puppeteer * puppeteer vs 12 seems stable without to many changes needed * normalize port numbers in snapshots * Handle scroll and ViewportResize events in cross origin iframe * Correctly map cross origin mutations * Map selection events for cross origin iframes * Map canvas mutations for cross origin iframes * Update snapshot to include canvas events * Skip all meta events * Support custom events as best we can in cross origin iframes * Use earliest version of puppeteer that works with cross origin live-stream * Map mouse/touch interaction events * Update snapshots for correctly mapped click events * Tweak tests for new puppeteer version * Map MediaInteraction correctly for cross origin iframes * Make tests consistent between high and low dpi devices * Make test less flaky * Make test less flaky * Make test less flaky * Make test less flaky * Add support for styles in cross origin iframes * Map traditional stylesheet mutations on cross origin iframes * Add todo * Add iframe mirror * Get iframe manager to use iframe mirrors internally * Rename `IframeMirror` to `CrossOriginIframeMirror` * Setup basic cross origin canvas webrtc streaming * Clean up removed canvas elements * reset style mirror on new full snapshot * Fix cross origin canvas webrtc streaming * Make emit optional * Run tests on github actions * Upload image artifacts from failed tests * Use newer github actions * Test: hopefully adding more wait will fix it * add extra wait * Fix image snapshot tests * Make tests run with new puppeteer version * upgrade eslint-plugin-jest * Chore: Remove travis ci as ci's running on github actions * Chore: Support recording cross origin iframe in repl * Force developers to update the cross origin iframe mapping when adding new events https://github.com/rrweb-io/rrweb/pull/1035#discussion_r1012516277 * Document cross origin iframe recording * Docs: cross origin iframes recording methods * Docs: AI translated, cross origin iframe recording * rename getParentId to getId * Migrate to @rrweb/types * Run on pull request * doc: improve Chinese doc * Rename `parentId` to `Id` Co-authored-by: Mark-Fenng --- docs/recipes/cross-origin-iframes.md | 116 + docs/recipes/cross-origin-iframes.zh_CN.md | 113 + docs/recipes/dive-into-event.md | 2 +- guide.md | 53 +- guide.zh_CN.md | 53 +- package.json | 2 +- packages/rrweb-player/typings/index.d.ts | 2 +- packages/rrweb-snapshot/src/index.ts | 2 + packages/rrweb-snapshot/src/snapshot.ts | 2 +- packages/rrweb/package.json | 6 +- packages/rrweb/scripts/repl.js | 78 +- packages/rrweb/scripts/stream.js | 114 +- .../src/plugins/canvas-webrtc/record/index.ts | 265 +- .../src/plugins/canvas-webrtc/replay/index.ts | 4 +- .../src/record/cross-origin-iframe-mirror.ts | 88 + packages/rrweb/src/record/iframe-manager.ts | 228 +- packages/rrweb/src/record/index.ts | 55 +- packages/rrweb/src/record/mutation.ts | 2 +- packages/rrweb/src/replay/index.ts | 2 +- packages/rrweb/src/types.ts | 12 +- packages/rrweb/src/utils.ts | 4 + .../__snapshots__/integration.test.ts.snap | 241 +- .../test/__snapshots__/record.test.ts.snap | 950 ++-- ...record-and-replay-a-webgl-image-1-snap.png | Bin 10913 -> 10752 bytes ...ecord-and-replay-a-webgl-square-1-snap.png | Bin 10812 -> 10650 bytes packages/rrweb/test/e2e/webgl.test.ts | 22 +- .../test/html/assets/1-minute-of-silence.mp3 | Bin 0 -> 96246 bytes packages/rrweb/test/html/assets/style.css | 3 + packages/rrweb/test/html/audio.html | 16 + .../rrweb/test/html/blocked-unblocked.html | 108 +- packages/rrweb/test/html/hello-world.html | 12 + packages/rrweb/test/integration.test.ts | 146 +- packages/rrweb/test/record.test.ts | 309 +- .../cross-origin-iframes.test.ts.snap | 4496 +++++++++++++++++ .../test/record/cross-origin-iframes.test.ts | 514 ++ packages/rrweb/test/utils.ts | 5 + packages/types/src/index.ts | 36 +- yarn.lock | 135 +- 38 files changed, 7260 insertions(+), 936 deletions(-) create mode 100644 docs/recipes/cross-origin-iframes.md create mode 100644 docs/recipes/cross-origin-iframes.zh_CN.md create mode 100644 packages/rrweb/src/record/cross-origin-iframe-mirror.ts create mode 100644 packages/rrweb/test/html/assets/1-minute-of-silence.mp3 create mode 100644 packages/rrweb/test/html/assets/style.css create mode 100644 packages/rrweb/test/html/audio.html create mode 100644 packages/rrweb/test/html/hello-world.html create mode 100644 packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap create mode 100644 packages/rrweb/test/record/cross-origin-iframes.test.ts diff --git a/docs/recipes/cross-origin-iframes.md b/docs/recipes/cross-origin-iframes.md new file mode 100644 index 0000000000..89c01dee6e --- /dev/null +++ b/docs/recipes/cross-origin-iframes.md @@ -0,0 +1,116 @@ +# Cross origin iframes + +By default browsers make it difficult to access the contents of an iframe that is hosted on a different domain. This is a security feature to prevent malicious sites from accessing sensitive information on other sites. It is possible to work around this security feature, but it is not recommended unless you [are very strict](https://stackoverflow.com/a/21629575) about allowing only the sites you trust to embed your website inside of an iframe. +Since if you allow recording cross origin iframes, any malicious website can embed your website and as long as they have rrweb running they can record all the contents of your website. + +## How to record cross origin iframes + +Enable recording cross-origin iframes in your parent page: + +```js +rrweb.record({ + emit(event) {}, // all events will be emitted here, including events from cross origin iframes + recordCrossOriginIframes: true, +}); +``` + +Enable replaying cross-origin iframes in your child page: + +```js +rrweb.record({ + emit(event) {}, // this is required for rrweb, but the child page will not emit any events + recordCrossOriginIframes: true, +}); +``` + +## Considerations + +When cross origin iframe recording is turned on rrweb will check to see if it is being run in a top level window. +If it isn't it'll send the events to the parent window via `postMessage`. + +If you don't have rrweb running in the top level window, the events will be lost when `recordCrossOriginIframes` is turned on. + +If the top level window is a malicious website it can listen to the events and send them to a server of its choosing. + +Or if a malicious script is running in on your page they can listen in on `postMessage` and as communication between the child and parent window is not encrypted. And they can see the events. + +## Options for injecting rrweb into cross origin iframes + +### 1. Website owners, add rrweb in the iframes + +If you own the website that with the iframe and the website that is being embedded in an iframe, you can add rrweb to both pages via a script tag. + +### 2. Browser extension + +See https://developer.chrome.com/docs/extensions/mv3/content_scripts/#functionality + +### 3. Puppeteer script + +```js +import puppeteer from 'puppeteer'; + +async function injectRecording(frame) { + await frame.evaluate((rrwebCode) => { + if (window.__IS_RECORDING__) return; + window.__IS_RECORDING__ = true; + + (async () => { + function loadScript(code) { + const s = document.createElement('script'); + s.type = 'text/javascript'; + s.innerHTML = code; + if (document.head) { + document.head.append(s); + } else { + requestAnimationFrame(() => { + document.head.append(s); + }); + } + } + loadScript(rrwebCode); + + window.rrweb.record({ + emit: (event) => { + window._captureEvent(event); + }, + recordCrossOriginIframes: true, + }); + })(); + }, code); +} + +const browser = await puppeteer.launch(); +const page = (await browser.pages())[0]; + +const events = []; // contains all events from all frames + +await page.exposeFunction('_captureEvent', (event) => { + events.push(event); +}); + +page.on('framenavigated', async (frame) => { + await injectRecording(frame); // injects rrweb into the iframe +}); + +await page.goto('https://example.com'); + +// your events are in the events array +``` + +### 4. Electron + +```ts +const win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'rrweb-recording-script.js'), + // this turns on preload inside iframes, but disables node integration + nodeIntegrationInSubFrames: true, + nodeIntegration: false, + }, +}); +``` + +See https://www.electronjs.org/docs/latest/tutorial/tutorial-preload +And https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts diff --git a/docs/recipes/cross-origin-iframes.zh_CN.md b/docs/recipes/cross-origin-iframes.zh_CN.md new file mode 100644 index 0000000000..b56f95420a --- /dev/null +++ b/docs/recipes/cross-origin-iframes.zh_CN.md @@ -0,0 +1,113 @@ +# 跨域 IFrame 录制 + +默认情况下,浏览器很难访问托管在不同域上的 iframe 的内容。 这是一项安全功能,可防止恶意站点访问其他站点上的敏感信息。 rrweb 提供了一个解决方案,但如果您对网站的安全有严格的要求https://stackoverflow.com/a/21629575, 只允许您信任的网站将您的网站嵌入 iframe 中,那我们不建议使用我们的解决方案。 +因为如果您允许记录跨源 iframe,任何恶意网站都可以嵌入您的网站,并且只要它们运行 rrweb,它们就可以记录您网站的所有内容。 + +## 如何记录跨源 iframe + +在父页面中启用录制跨域 iframe: + +```js +rrweb.record({ + emit(event) {}, // 所有事件都将在此处发出,包括来自跨源 iframe 的事件 + recordCrossOriginIframes: true, +}); +``` + +在您的子页面中启用重放跨域 iframe: + +```js +rrweb.record({ + emit(event) {}, // 这是 rrweb 所必需的,但子页面不会发出任何事件 + recordCrossOriginIframes: true, +}); +``` + +## 注意事项 + +当跨源 iframe 录制开启时,rrweb 将检查它是否正在顶级窗口中运行。 如果不是,它将通过 `postMessage` 将事件发送到父窗口。 + +如果您没有在顶层窗口中运行 rrweb,则打开 `recordCrossOriginIframes` 时事件将丢失。 + +如果顶层窗口是一个恶意网站,它可以监听事件并将它们发送到它选择的服务器。或者如果您的页面上正在运行恶意脚本,则它们可以监听“postMessage”,又因为子窗口和父窗口之间的通信未加密,所以他们可以获取到所有事件。 + +## 将 rrweb 注入跨源 iframe 的选项 + +### 1. 网站所有者,在 iframe 中添加 rrweb + +如果您拥有使用 iframe 的网站和嵌入在 iframe 中的网站,您可以通过脚本标签将 rrweb 添加到这两个页面。 + +### 2. 浏览器扩展 + +请查看 https://developer.chrome.com/docs/extensions/mv3/content_scripts/#functionality + +### 3. Puppeteer script + +```js +import puppeteer from 'puppeteer'; + +async function injectRecording(frame) { + await frame.evaluate((rrwebCode) => { + if (window.__IS_RECORDING__) return; + window.__IS_RECORDING__ = true; + + (async () => { + function loadScript(code) { + const s = document.createElement('script'); + s.type = 'text/javascript'; + s.innerHTML = code; + if (document.head) { + document.head.append(s); + } else { + requestAnimationFrame(() => { + document.head.append(s); + }); + } + } + loadScript(rrwebCode); + + window.rrweb.record({ + emit: (event) => { + window._captureEvent(event); + }, + recordCrossOriginIframes: true, + }); + })(); + }, code); +} + +const browser = await puppeteer.launch(); +const page = (await browser.pages())[0]; + +const events = []; // 包含来自所有Frame的所有事件 + +await page.exposeFunction('_captureEvent', (event) => { + events.push(event); +}); + +page.on('framenavigated', async (frame) => { + await injectRecording(frame); // 将 rrweb 注入 iframe +}); + +await page.goto('https://example.com'); + +// 您的事件将在事件数组中 +``` + +### 4. Electron + +```ts +const win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'rrweb-recording-script.js'), + // 这会打开 iframe 内的预加载,但会禁用节点集成 + nodeIntegrationInSubFrames: true, + nodeIntegration: false, + }, +}); +``` + +请查看 https://www.electronjs.org/docs/latest/tutorial/tutorial-preload +和 https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts diff --git a/docs/recipes/dive-into-event.md b/docs/recipes/dive-into-event.md index 4df9152c19..be63972a68 100644 --- a/docs/recipes/dive-into-event.md +++ b/docs/recipes/dive-into-event.md @@ -67,4 +67,4 @@ source -> IncrementalSource.Font data -> fontData ``` -enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/master/packages/rrweb/typings/types.d.ts#L62). +enum IncrementalSource's definition can be found in this [list](https://github.com/rrweb-io/rrweb/blob/98e71cd0d23628cd1fbdbe47664a65748084c4a4/packages/types/src/index.ts#L69). diff --git a/guide.md b/guide.md index 206fa053ab..76f781d096 100644 --- a/guide.md +++ b/guide.md @@ -135,32 +135,33 @@ setInterval(save, 10 * 1000); The parameter of `rrweb.record` accepts the following options. -| key | default | description | -| -------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| emit | required | the callback function to get emitted events | -| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | -| checkoutEveryNms | - | take a full snapshot after every N ms
refer to the [checkout](#checkout) chapter | -| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | -| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | -| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | -| ignoreCSSAttributes | null | array of CSS attributes that should be ignored | -| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | -| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | -| maskAllInputs | false | mask all input content as \* | -| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | -| maskInputFn | - | customize mask input content recording logic | -| maskTextFn | - | customize mask text content recording logic | -| slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | -| dataURLOptions | {} | Canvas image format and quality ,This parameter will be passed to the OffscreenCanvas.convertToBlob(),Using this parameter effectively reduces the size of the recorded data | -| inlineStylesheet | true | whether to inline the stylesheet in the events | -| hooks | {} | hooks for events
refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | -| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| recordCanvas | false | Whether to record the canvas element. Available options:
`false`,
`true` | -| inlineImages | false | whether to record the image content | -| collectFonts | false | whether to collect fonts in the website | -| userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) | -| plugins | [] | load plugins to provide extended record functions. [What is plugins?](./docs/recipes/plugin.md) | +| key | default | description | +| ------------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| emit | required | the callback function to get emitted events | +| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | +| checkoutEveryNms | - | take a full snapshot after every N ms
refer to the [checkout](#checkout) chapter | +| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter | +| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter | +| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter | +| ignoreCSSAttributes | null | array of CSS attributes that should be ignored | +| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter | +| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | +| maskAllInputs | false | mask all input content as \* | +| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | +| maskInputFn | - | customize mask input content recording logic | +| maskTextFn | - | customize mask text content recording logic | +| slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | +| dataURLOptions | {} | Canvas image format and quality ,This parameter will be passed to the OffscreenCanvas.convertToBlob(),Using this parameter effectively reduces the size of the recorded data | +| inlineStylesheet | true | whether to inline the stylesheet in the events | +| hooks | {} | hooks for events
refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | +| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| recordCanvas | false | Whether to record the canvas element. Available options:
`false`,
`true` | +| recordCrossOriginIframes | false | Whether to record cross origin iframes. rrweb has to be injected in each child iframe for this to work. Available options:
`false`,
`true` | +| inlineImages | false | whether to record the image content | +| collectFonts | false | whether to collect fonts in the website | +| userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) | +| plugins | [] | load plugins to provide extended record functions. [What is plugins?](./docs/recipes/plugin.md) | #### Privacy diff --git a/guide.zh_CN.md b/guide.zh_CN.md index e867c93de6..1093dbb386 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -131,32 +131,33 @@ setInterval(save, 10 * 1000); `rrweb.record(config)` 的 config 部分接受以下参数 -| key | 默认值 | 功能 | -| -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| emit | 必填 | 获取当前录制的数据 | -| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | -| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | -| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | -| ignoreCSSAttributes | null | 应该被忽略的 CSS 属性数组 | -| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | -| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | -| maskAllInputs | false | 将所有输入内容记录为 \* | -| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | -| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | -| maskTextFn | - | 自定义文字内容的记录逻辑 | -| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | -| inlineStylesheet | true | 是否将样式表内联 | -| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | -| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 | -| recordCanvas | false | 是否记录 canvas 内容, 可用选项:false, true | -| inlineImages | false | 是否将图片内容记内联录制 | -| collectFonts | false | 是否记录页面中的字体文件 | -| userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | -| plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | +| key | 默认值 | 功能 | +| ------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| emit | 必填 | 获取当前录制的数据 | +| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | +| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 | +| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | +| ignoreCSSAttributes | null | 应该被忽略的 CSS 属性数组 | +| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 | +| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 | +| maskAllInputs | false | 将所有输入内容记录为 \* | +| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | +| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | +| maskTextFn | - | 自定义文字内容的记录逻辑 | +| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | +| inlineStylesheet | true | 是否将样式表内联 | +| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | +| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 | +| recordCanvas | false | 是否记录 canvas 内容, 可用选项:`false`, `true` | +| recordCrossOriginIframes | false | 是否记录 cross origin iframes。 必须在每个子 iframe 中注入 rrweb 才能使其工作。 可用选项:`false`, `true` | +| inlineImages | false | 是否将图片内容记内联录制 | +| collectFonts | false | 是否记录页面中的字体文件 | +| userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | +| plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | #### 隐私 diff --git a/package.json b/package.json index 66ae51cc19..7192130275 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "concurrently": "^7.1.0", "eslint": "^8.19.0", "eslint-plugin-compat": "^4.0.2", - "eslint-plugin-jest": "^26.5.3", + "eslint-plugin-jest": "^27.1.3", "eslint-plugin-tsdoc": "^0.2.16", "lerna": "^4.0.0", "markdownlint": "^0.25.1", diff --git a/packages/rrweb-player/typings/index.d.ts b/packages/rrweb-player/typings/index.d.ts index c3c3b80277..b27799aa88 100755 --- a/packages/rrweb-player/typings/index.d.ts +++ b/packages/rrweb-player/typings/index.d.ts @@ -1,5 +1,5 @@ import { playerConfig } from 'rrweb/typings/types'; -import type { eventWithTime } from '@rrweb/types'; +import { eventWithTime } from '@rrweb/types'; import { Replayer, mirror } from 'rrweb'; import { SvelteComponent } from 'svelte'; diff --git a/packages/rrweb-snapshot/src/index.ts b/packages/rrweb-snapshot/src/index.ts index 82dd6a42c3..b2417187ca 100644 --- a/packages/rrweb-snapshot/src/index.ts +++ b/packages/rrweb-snapshot/src/index.ts @@ -6,6 +6,7 @@ import snapshot, { needMaskingText, classMatchesRegex, IGNORED_NODE, + genId, } from './snapshot'; import rebuild, { buildNodeWithSN, @@ -28,4 +29,5 @@ export { needMaskingText, classMatchesRegex, IGNORED_NODE, + genId, }; diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 3708001599..99a23ff7be 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -27,7 +27,7 @@ const tagNameRegex = new RegExp('[^a-z0-9-_:]'); export const IGNORED_NODE = -2; -function genId(): number { +export function genId(): number { return _id++; } diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index a51c2af12c..8e09e593b7 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -50,7 +50,7 @@ "@types/dom-mediacapture-transform": "^0.1.3", "@types/inquirer": "^8.2.1", "@types/jest": "^27.4.1", - "@types/jest-image-snapshot": "^4.3.1", + "@types/jest-image-snapshot": "^5.1.0", "@types/node": "^17.0.21", "@types/offscreencanvas": "^2019.6.4", "@types/prettier": "^2.3.2", @@ -63,10 +63,10 @@ "ignore-styles": "^5.0.1", "inquirer": "^9.0.0", "jest": "^27.5.1", - "jest-image-snapshot": "^4.5.1", + "jest-image-snapshot": "^5.2.0", "jest-snapshot": "^23.6.0", "prettier": "2.2.1", - "puppeteer": "^9.1.1", + "puppeteer": "^11.0.0", "rollup": "^2.68.0", "rollup-plugin-esbuild": "^4.9.1", "rollup-plugin-postcss": "^3.1.1", diff --git a/packages/rrweb/scripts/repl.js b/packages/rrweb/scripts/repl.js index 4edb07a3c3..0bf4a7de62 100644 --- a/packages/rrweb/scripts/repl.js +++ b/packages/rrweb/scripts/repl.js @@ -21,6 +21,43 @@ void (async () => { const code = getCode(); let events = []; + async function injectRecording(frame) { + await frame.evaluate((rrwebCode) => { + const win = window; + if (win.__IS_RECORDING__) return; + win.__IS_RECORDING__ = true; + + (async () => { + function loadScript(code) { + const s = document.createElement('script'); + let r = false; + s.type = 'text/javascript'; + s.innerHTML = code; + if (document.head) { + document.head.append(s); + } else { + requestAnimationFrame(() => { + document.head.append(s); + }); + } + } + loadScript(rrwebCode); + + win.events = []; + rrweb.record({ + emit: (event) => { + win.events.push(event); + win._replLog(event); + }, + plugins: [], + recordCanvas: true, + recordCrossOriginIframes: true, + collectFonts: true, + }); + })(); + }, code); + } + await start('https://react-redux.realworld.io'); const fakeGoto = async (page, url) => { @@ -44,8 +81,7 @@ void (async () => { { type: 'input', name: 'url', - message: - `Enter the url you want to record, e.g [${defaultURL}]: `, + message: `Enter the url you want to record, e.g [${defaultURL}]: `, }, ]); @@ -116,34 +152,18 @@ void (async () => { ], }); const page = await browser.newPage(); - await page.goto(url, { - waitUntil: 'domcontentloaded', - timeout: 300000, - }); await page.exposeFunction('_replLog', (event) => { events.push(event); }); - await page.evaluate(`;${code} - window.__IS_RECORDING__ = true - rrweb.record({ - emit: event => window._replLog(event), - recordCanvas: true, - collectFonts: true - }); - `); - page.on('framenavigated', async () => { - const isRecording = await page.evaluate('window.__IS_RECORDING__'); - if (!isRecording) { - await page.evaluate(`;${code} - window.__IS_RECORDING__ = true - rrweb.record({ - emit: event => window._replLog(event), - recordCanvas: true, - collectFonts: true - }); - `); - } + + page.on('framenavigated', async (frame) => { + await injectRecording(frame); + }); + + await page.goto(url, { + waitUntil: 'domcontentloaded', + timeout: 300000, }); emitter.once('done', async (shouldReplay) => { @@ -211,9 +231,9 @@ void (async () => { -
-

- Verify that block class bugs are fixed -

-
+
+

Verify that block class bugs are fixed

+
-


+


-


+


-


+


-


+


-


+


diff --git a/packages/rrweb/test/html/hello-world.html b/packages/rrweb/test/html/hello-world.html new file mode 100644 index 0000000000..04c1907d6a --- /dev/null +++ b/packages/rrweb/test/html/hello-world.html @@ -0,0 +1,12 @@ + + + + + + + Hello World! + + + Hello world! + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 7df01ca26d..82ce7d1933 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -73,7 +73,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('textarea', 'textarea test'); await page.select('select', '1'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -91,7 +93,9 @@ describe('record integration tests', function (this: ISuite) { p.appendChild(document.createElement('span')); }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -111,7 +115,9 @@ describe('record integration tests', function (this: ISuite) { p.innerText = 'mutated'; }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -129,7 +135,9 @@ describe('record integration tests', function (this: ISuite) { document.body.setAttribute('test', 'true'); }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -146,7 +154,9 @@ describe('record integration tests', function (this: ISuite) { await page.evaluate( 'document.getElementById("select2-drop").setAttribute("style", document.getElementById("select2-drop").style.cssText + "color:black !important")', ); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -180,7 +190,9 @@ describe('record integration tests', function (this: ISuite) { await waitForRAF(page); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -191,7 +203,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('.rr-ignore', 'secret'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -209,7 +223,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('textarea', 'textarea test'); await page.select('select', '1'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -233,7 +249,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('input[type="password"]', 'password'); await page.select('select', '1'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -250,7 +268,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('input[type="password"]', 'secr3t'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -268,7 +288,9 @@ describe('record integration tests', function (this: ISuite) { await page.type('textarea', 'textarea test'); await page.select('select', '1'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -281,7 +303,9 @@ describe('record integration tests', function (this: ISuite) { await page.evaluate(`document.getElementById('text').innerText = '1'`); await page.click('#text'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -301,7 +325,9 @@ describe('record integration tests', function (this: ISuite) { nextElement.parentNode!.insertBefore(el, nextElement); }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -310,13 +336,19 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about: blank'); await page.setContent(getHtml.call(this, 'blocked-unblocked.html')); - const elements1 = await page.$x('/html/body/div[1]/button'); + const elements1 = (await page.$x( + '/html/body/div[1]/button', + )) as puppeteer.ElementHandle[]; await elements1[0].click(); - const elements2 = await page.$x('/html/body/div[2]/button'); + const elements2 = (await page.$x( + '/html/body/div[2]/button', + )) as puppeteer.ElementHandle[]; await elements2[0].click(); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -334,7 +366,9 @@ describe('record integration tests', function (this: ISuite) { p.removeChild(span); div.appendChild(span); }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -349,7 +383,9 @@ describe('record integration tests', function (this: ISuite) { document.body.appendChild(div); div.appendChild(span); }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -358,7 +394,9 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'react-styled-components.html')); await page.click('.toggle'); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -371,7 +409,9 @@ describe('record integration tests', function (this: ISuite) { }), ); await waitForRAF(page); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; for (const event of snapshots) { if (event.type === EventType.FullSnapshot) { visitSnapshot(event.data.node, (n) => { @@ -393,7 +433,9 @@ describe('record integration tests', function (this: ISuite) { }), ); await page.waitForTimeout(50); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -406,7 +448,9 @@ describe('record integration tests', function (this: ISuite) { }), ); await waitForRAF(page); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -425,7 +469,9 @@ describe('record integration tests', function (this: ISuite) { } }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -483,11 +529,15 @@ describe('record integration tests', function (this: ISuite) { document.body.appendChild(iframe); }); + await waitForRAF(page); await page.frames()[1].evaluate(() => { console.log('from iframe'); }); + await waitForRAF(page); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -504,7 +554,9 @@ describe('record integration tests', function (this: ISuite) { await page.waitForTimeout(50); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -519,7 +571,9 @@ describe('record integration tests', function (this: ISuite) { await page.waitForSelector('img'); // wait for image to get added await waitForRAF(page); // wait for image to be captured - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -534,7 +588,9 @@ describe('record integration tests', function (this: ISuite) { await page.waitForTimeout(50); // wait for image to get added await waitForRAF(page); // wait for image to be captured - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -555,7 +611,9 @@ describe('record integration tests', function (this: ISuite) { await page.waitForTimeout(50); // wait for image to get added await waitForRAF(page); // wait for image to be captured - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -603,7 +661,9 @@ describe('record integration tests', function (this: ISuite) { }); await page.waitForTimeout(50); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -646,7 +706,9 @@ describe('record integration tests', function (this: ISuite) { }); await waitForRAF(page); // wait till browser sent snapshots - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -674,7 +736,9 @@ describe('record integration tests', function (this: ISuite) { }); await waitForRAF(page); // wait for snapshot to be updated - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -706,7 +770,9 @@ describe('record integration tests', function (this: ISuite) { }); await waitForRAF(page); // wait till browser sent snapshots - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -746,7 +812,9 @@ describe('record integration tests', function (this: ISuite) { }); await waitForRAF(page); // wait till browser sent snapshots - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -759,7 +827,9 @@ describe('record integration tests', function (this: ISuite) { }), ); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -773,7 +843,9 @@ describe('record integration tests', function (this: ISuite) { }), ); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); @@ -794,7 +866,9 @@ describe('record integration tests', function (this: ISuite) { p.innerText = 'mutated'; }); - const snapshots = await page.evaluate('window.snapshots'); + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; assertSnapshot(snapshots); }); }); diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 515bdfc1f8..215067d2da 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -11,7 +11,14 @@ import { styleSheetRuleData, selectionData, } from '@rrweb/types'; -import { assertSnapshot, launchPuppeteer, waitForRAF } from './utils'; +import { + assertSnapshot, + getServerURL, + launchPuppeteer, + startServer, + waitForRAF, +} from './utils'; +import type { Server } from 'http'; interface ISuite { code: string; @@ -465,54 +472,63 @@ describe('record', function (this: ISuite) { it('captures mutations on adopted stylesheets', async () => { await ctx.page.evaluate(() => { - document.body.innerHTML = ` + return new Promise((resolve) => { + document.body.innerHTML = `
div in outermost document
`; - const sheet = new CSSStyleSheet(); - // Add stylesheet to a document. + const sheet = new CSSStyleSheet(); + // Add stylesheet to a document. - document.adoptedStyleSheets = [sheet]; + document.adoptedStyleSheets = [sheet]; - const iframe = document.querySelector('iframe'); - const sheet2 = new (iframe!.contentWindow! as Window & - typeof globalThis).CSSStyleSheet(); + const iframe = document.querySelector('iframe'); + const sheet2 = new (iframe!.contentWindow! as Window & + typeof globalThis).CSSStyleSheet(); - // Add stylesheet to an IFrame document. - iframe!.contentDocument!.adoptedStyleSheets = [sheet2]; - iframe!.contentDocument!.body.innerHTML = '

h1 in iframe

'; + // Add stylesheet to an IFrame document. + iframe!.contentDocument!.adoptedStyleSheets = [sheet2]; + iframe!.contentDocument!.body.innerHTML = '

h1 in iframe

'; - const { record } = ((window as unknown) as IWindow).rrweb; - record({ - emit: ((window as unknown) as IWindow).emit, - }); + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + emit: ((window as unknown) as IWindow).emit, + }); - setTimeout(() => { - sheet.replace!('div { color: yellow; }'); - sheet2.replace!('h1 { color: blue; }'); - }, 0); + setTimeout(() => { + sheet.replace!('div { color: yellow; }'); + sheet2.replace!('h1 { color: blue; }'); + }, 0); - setTimeout(() => { - sheet.replaceSync!('div { display: inline ; }'); - sheet2.replaceSync!('h1 { font-size: large; }'); - }, 5); + setTimeout(() => { + sheet.replaceSync!('div { display: inline ; }'); + sheet2.replaceSync!('h1 { font-size: large; }'); + }, 5); - setTimeout(() => { - (sheet.cssRules[0] as CSSStyleRule).style.setProperty('color', 'green'); - (sheet.cssRules[0] as CSSStyleRule).style.removeProperty('display'); - (sheet2.cssRules[0] as CSSStyleRule).style.setProperty( - 'font-size', - 'medium', - 'important', - ); - sheet2.insertRule('h2 { color: red; }'); - }, 10); + setTimeout(() => { + (sheet.cssRules[0] as CSSStyleRule).style.setProperty( + 'color', + 'green', + ); + (sheet.cssRules[0] as CSSStyleRule).style.removeProperty('display'); + (sheet2.cssRules[0] as CSSStyleRule).style.setProperty( + 'font-size', + 'medium', + 'important', + ); + sheet2.insertRule('h2 { color: red; }'); + }, 10); - setTimeout(() => { - sheet.insertRule('body { border: 2px solid blue; }', 1); - sheet2.deleteRule(0); - }, 15); + setTimeout(() => { + sheet.insertRule('body { border: 2px solid blue; }', 1); + sheet2.deleteRule(0); + }, 15); + + setTimeout(() => { + resolve(undefined); + }, 20); + }); }); await waitForRAF(ctx.page); assertSnapshot(ctx.events); @@ -602,70 +618,91 @@ describe('record', function (this: ISuite) { assertSnapshot(ctx.events); }); - it('captures stylesheets that are still loading', async () => { - await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + describe('loading stylesheets', () => { + let server: Server; + let serverURL: string; - record({ - inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + beforeAll(async () => { + server = await startServer(); + serverURL = getServerURL(server); + }); + + beforeEach(async () => { + ctx.page = await ctx.browser.newPage(); + await ctx.page.goto(`${serverURL}/html/hello-world.html`); + await ctx.page.evaluate(ctx.code); + ctx.events = []; + await ctx.page.exposeFunction('emit', (e: eventWithTime) => { + if ( + e.type === EventType.DomContentLoaded || + e.type === EventType.Load + ) { + return; + } + ctx.events.push(e); }); - const link1 = document.createElement('link'); - link1.setAttribute('rel', 'stylesheet'); - link1.setAttribute( - 'href', - URL.createObjectURL( - new Blob(['body { color: pink; }'], { - type: 'text/css', - }), - ), - ); - document.head.appendChild(link1); + ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); }); - // `blob:` URLs are not available immediately, so we need to wait for the browser to load them - await waitForRAF(ctx.page); - // 'blob' URL is different in every execution so we need to remove it from the snapshot. - const filteredEvents = JSON.parse( - JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'), - ); - assertSnapshot(filteredEvents); - }); + afterAll(async () => { + await server.close(); + }); - it('captures stylesheets in iframes that are still loading', async () => { - await ctx.page.evaluate(() => { - const iframe = document.createElement('iframe'); - iframe.setAttribute('src', 'about:blank'); - document.body.appendChild(iframe); - const iframeDoc = iframe.contentDocument!; + it('captures stylesheets that are still loading', async () => { + ctx.page.evaluate((serverURL) => { + const { record } = ((window as unknown) as IWindow).rrweb; - const { record } = ((window as unknown) as IWindow).rrweb; + record({ + inlineStylesheet: true, + emit: ((window as unknown) as IWindow).emit, + }); - record({ - inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, - }); + const link1 = document.createElement('link'); + link1.setAttribute('rel', 'stylesheet'); + link1.setAttribute('href', `${serverURL}/html/assets/style.css`); + document.head.appendChild(link1); + }, serverURL); - const linkEl = document.createElement('link'); - linkEl.setAttribute('rel', 'stylesheet'); - linkEl.setAttribute( - 'href', - URL.createObjectURL( - new Blob(['body { color: pink; }'], { - type: 'text/css', - }), - ), - ); - iframeDoc.head.appendChild(linkEl); + await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`); + await waitForRAF(ctx.page); + + assertSnapshot(ctx.events); }); - // `blob:` URLs are not available immediately, so we need to wait for the browser to load them - await waitForRAF(ctx.page); - const filteredEvents = JSON.parse( - JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'), - ); - assertSnapshot(filteredEvents); + it('captures stylesheets in iframes that are still loading', async () => { + ctx.page.evaluate(() => { + const iframe = document.createElement('iframe'); + iframe.setAttribute('src', `/html/hello-world.html?2`); + document.body.appendChild(iframe); + + const { record } = ((window as unknown) as IWindow).rrweb; + + record({ + inlineStylesheet: true, + emit: ((window as unknown) as IWindow).emit, + }); + }); + + await ctx.page.waitForResponse(`${serverURL}/html/hello-world.html?2`); + + await waitForRAF(ctx.page); + + ctx.page.evaluate(() => { + const iframe = document.querySelector('iframe')!; + const iframeDoc = iframe.contentDocument!; + const linkEl = document.createElement('link'); + linkEl.setAttribute('rel', 'stylesheet'); + linkEl.setAttribute('href', `/html/assets/style.css`); + iframeDoc.head.appendChild(linkEl); + }); + + await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`); + + await waitForRAF(ctx.page); + + assertSnapshot(ctx.events); + }); }); it('captures CORS stylesheets that are still loading', async () => { @@ -695,65 +732,71 @@ describe('record', function (this: ISuite) { it('captures adopted stylesheets in shadow doms and iframe', async () => { await ctx.page.evaluate(() => { - document.body.innerHTML = ` + return new Promise((resolve) => { + document.body.innerHTML = `
div in outermost document
`; - const sheet = new CSSStyleSheet(); - sheet.replaceSync!( - 'div { color: yellow; } h2 { color: orange; } h3 { font-size: larger;}', - ); - // Add stylesheet to a document. + const sheet = new CSSStyleSheet(); + sheet.replaceSync!( + 'div { color: yellow; } h2 { color: orange; } h3 { font-size: larger;}', + ); + // Add stylesheet to a document. - document.adoptedStyleSheets = [sheet]; + document.adoptedStyleSheets = [sheet]; - // Add stylesheet to a shadow host. - const host = document.querySelector('#shadow-host1'); - const shadow = host!.attachShadow({ mode: 'open' }); - shadow.innerHTML = - '
div in shadow dom 1
span in shadow dom 1'; - const sheet2 = new CSSStyleSheet(); + // Add stylesheet to a shadow host. + const host = document.querySelector('#shadow-host1'); + const shadow = host!.attachShadow({ mode: 'open' }); + shadow.innerHTML = + '
div in shadow dom 1
span in shadow dom 1'; + const sheet2 = new CSSStyleSheet(); - sheet2.replaceSync!('span { color: red; }'); + sheet2.replaceSync!('span { color: red; }'); - shadow.adoptedStyleSheets = [sheet, sheet2]; + shadow.adoptedStyleSheets = [sheet, sheet2]; - // Add stylesheet to an IFrame document. - const iframe = document.querySelector('iframe'); - const sheet3 = new (iframe!.contentWindow! as IWindow & - typeof globalThis).CSSStyleSheet(); - sheet3.replaceSync!('h1 { color: blue; }'); - - iframe!.contentDocument!.adoptedStyleSheets = [sheet3]; + // Add stylesheet to an IFrame document. + const iframe = document.querySelector('iframe'); + const sheet3 = new (iframe!.contentWindow! as IWindow & + typeof globalThis).CSSStyleSheet(); + sheet3.replaceSync!('h1 { color: blue; }'); - const ele = iframe!.contentDocument!.createElement('h1'); - ele.innerText = 'h1 in iframe'; - iframe!.contentDocument!.body.appendChild(ele); + iframe!.contentDocument!.adoptedStyleSheets = [sheet3]; - ((window as unknown) as IWindow).rrweb.record({ - emit: ((window.top as unknown) as IWindow).emit, - }); + const ele = iframe!.contentDocument!.createElement('h1'); + ele.innerText = 'h1 in iframe'; + iframe!.contentDocument!.body.appendChild(ele); - // Make incremental changes to shadow dom. - setTimeout(() => { - const host = document.querySelector('#shadow-host2'); - const shadow = host!.attachShadow({ mode: 'open' }); - shadow.innerHTML = - '
div in shadow dom 2
span in shadow dom 2'; - const sheet4 = new CSSStyleSheet(); - sheet4.replaceSync!('span { color: green; }'); - shadow.adoptedStyleSheets = [sheet, sheet4]; + ((window as unknown) as IWindow).rrweb.record({ + emit: ((window.top as unknown) as IWindow).emit, + }); - document.adoptedStyleSheets = [sheet4, sheet, sheet2]; + // Make incremental changes to shadow dom. + setTimeout(() => { + const host = document.querySelector('#shadow-host2'); + const shadow = host!.attachShadow({ mode: 'open' }); + shadow.innerHTML = + '
div in shadow dom 2
span in shadow dom 2'; + const sheet4 = new CSSStyleSheet(); + sheet4.replaceSync!('span { color: green; }'); + shadow.adoptedStyleSheets = [sheet, sheet4]; + + document.adoptedStyleSheets = [sheet4, sheet, sheet2]; + + const sheet5 = new (iframe!.contentWindow! as IWindow & + typeof globalThis).CSSStyleSheet(); + sheet5.replaceSync!('h2 { color: purple; }'); + iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3]; + }, 10); - const sheet5 = new (iframe!.contentWindow! as IWindow & - typeof globalThis).CSSStyleSheet(); - sheet5.replaceSync!('h2 { color: purple; }'); - iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3]; - }, 10); + setTimeout(() => { + resolve(null); + }, 20); + }); }); await waitForRAF(ctx.page); // wait till events get sent diff --git a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap new file mode 100644 index 0000000000..f1c697d34c --- /dev/null +++ b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap @@ -0,0 +1,4496 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`cross origin iframes audio.html should emit contents of iframe once 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/audio.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Audio\\", + \\"id\\": 23 + } + ], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 26 + } + ], + \\"id\\": 25 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"h1\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1 minute of silence\\", + \\"id\\": 31 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 32 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"audio\\", + \\"attributes\\": { + \\"controls\\": \\"\\", + \\"rr_mediaState\\": \\"paused\\", + \\"rr_mediaCurrentTime\\": 0 + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"source\\", + \\"attributes\\": { + \\"src\\": \\"http://localhost:3030/html/assets/1-minute-of-silence.mp3\\", + \\"type\\": \\"audio/mpeg\\" + }, + \\"childNodes\\": [], + \\"id\\": 35 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n Your browser does not support the audio element.\\\\n \\", + \\"id\\": 36 + } + ], + \\"id\\": 33 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 37 + } + ], + \\"id\\": 28 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 7, + \\"type\\": 0, + \\"id\\": 33, + \\"currentTime\\": 0, + \\"volume\\": 1, + \\"muted\\": false, + \\"playbackRate\\": 1 + } + } +]" +`; + +exports[`cross origin iframes form.html should map input events correctly 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"style\\": \\"width: 400px; height: 400px;\\", + \\"rr_src\\": \\"http://localhost:3030/html/form.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"form fields\\", + \\"id\\": 23 + } + ], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 26 + } + ], + \\"id\\": 25 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\" + }, + \\"childNodes\\": [], + \\"id\\": 34 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 35 + } + ], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 39 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 40 + } + ], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 44 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 45 + } + ], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 50 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\" + }, + \\"childNodes\\": [], + \\"id\\": 54 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\", + \\"selected\\": true + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 62 + } + ], + \\"id\\": 61 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 65 + } + ], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 66 + } + ], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 67 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 68 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 70 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 71 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 72 + } + ], + \\"id\\": 69 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 73 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 74 + } + ], + \\"id\\": 28 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tes\\", + \\"isChecked\\": false, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"test\\", + \\"isChecked\\": false, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 34 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"off\\", + \\"isChecked\\": false, + \\"id\\": 44 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 39 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 49 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 71 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tex\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"text\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"texta\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textar\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textare\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea \\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea t\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea te\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea tes\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea test\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"1\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + } +]" +`; + +exports[`cross origin iframes form.html should map scroll events correctly 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"style\\": \\"width: 400px; height: 400px;\\", + \\"rr_src\\": \\"http://localhost:3030/html/form.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"form fields\\", + \\"id\\": 23 + } + ], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 26 + } + ], + \\"id\\": 25 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\" + }, + \\"childNodes\\": [], + \\"id\\": 34 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 35 + } + ], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 39 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 40 + } + ], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 44 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 45 + } + ], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 50 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\" + }, + \\"childNodes\\": [], + \\"id\\": 54 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\", + \\"selected\\": true + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 62 + } + ], + \\"id\\": 61 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 63 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 65 + } + ], + \\"id\\": 64 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 66 + } + ], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 67 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 68 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 70 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 71 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 72 + } + ], + \\"id\\": 69 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 73 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 74 + } + ], + \\"id\\": 28 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 9, + \\"attributes\\": { + \\"style\\": { + \\"width\\": \\"Npx\\", + \\"height\\": \\"Npx\\" + } + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 3, + \\"id\\": 11, + \\"x\\": 0, + \\"y\\": 10 + } + } +]" +`; + +exports[`cross origin iframes move-node.html captures mutations on adopted stylesheets 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 15, + \\"id\\": 1, + \\"styleIds\\": [ + 1 + ], + \\"styles\\": [ + { + \\"styleId\\": 1, + \\"rules\\": [] + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 15, + \\"id\\": 11, + \\"styleIds\\": [ + 2 + ], + \\"styles\\": [ + { + \\"styleId\\": 2, + \\"rules\\": [] + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 1, + \\"replace\\": \\"div { color: yellow; }\\" + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 2, + \\"replace\\": \\"h1 { color: blue; }\\" + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 1, + \\"replaceSync\\": \\"div { display: inline ; }\\" + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 2, + \\"replaceSync\\": \\"h1 { font-size: large; }\\" + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"styleId\\": 1, + \\"set\\": { + \\"property\\": \\"color\\", + \\"value\\": \\"green\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"styleId\\": 1, + \\"remove\\": { + \\"property\\": \\"display\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"styleId\\": 2, + \\"set\\": { + \\"property\\": \\"font-size\\", + \\"value\\": \\"medium\\", + \\"priority\\": \\"important\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 2, + \\"adds\\": [ + { + \\"rule\\": \\"h2 { color: red; }\\" + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 1, + \\"adds\\": [ + { + \\"rule\\": \\"body { border: 2px solid blue; }\\", + \\"index\\": 1 + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"styleId\\": 2, + \\"removes\\": [ + { + \\"index\\": 0 + } + ] + } + } +]" +`; + +exports[`cross origin iframes move-node.html captures mutations on stylesheets 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"style\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 33 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"style\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 34 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"id\\": 33, + \\"adds\\": [ + { + \\"rule\\": \\"div { color: yellow; }\\" + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"id\\": 34, + \\"adds\\": [ + { + \\"rule\\": \\"h1 { color: blue; }\\" + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"id\\": 33, + \\"set\\": { + \\"property\\": \\"color\\", + \\"value\\": \\"green\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"id\\": 33, + \\"remove\\": { + \\"property\\": \\"display\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 13, + \\"id\\": 34, + \\"set\\": { + \\"property\\": \\"font-size\\", + \\"value\\": \\"medium\\", + \\"priority\\": \\"important\\" + }, + \\"index\\": [ + 0 + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"id\\": 34, + \\"adds\\": [ + { + \\"rule\\": \\"h2 { color: red; }\\" + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"id\\": 33, + \\"adds\\": [ + { + \\"rule\\": \\"body { border: 2px solid blue; }\\", + \\"index\\": 1 + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 8, + \\"id\\": 34, + \\"removes\\": [ + { + \\"index\\": 0 + } + ] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record DOM attribute changes 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [ + { + \\"id\\": 24, + \\"attributes\\": { + \\"class\\": \\"added-class-name\\" + } + } + ], + \\"removes\\": [], + \\"adds\\": [] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record DOM node movement 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 17, + \\"id\\": 24 + } + ], + \\"adds\\": [ + { + \\"parentId\\": 24, + \\"nextId\\": 26, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + } + }, + { + \\"parentId\\": 24, + \\"nextId\\": 31, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 26 + } + }, + { + \\"parentId\\": 26, + \\"nextId\\": 28, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + } + }, + { + \\"parentId\\": 26, + \\"nextId\\": 30, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 28 + } + }, + { + \\"parentId\\": 28, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + }, + { + \\"parentId\\": 26, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + }, + { + \\"parentId\\": 24, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + }, + { + \\"parentId\\": 17, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 33 + } + }, + { + \\"parentId\\": 33, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 24 + } + } + ] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record DOM node removal 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 17, + \\"id\\": 24 + } + ], + \\"adds\\": [] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record DOM text changes 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [ + { + \\"id\\": 29, + \\"value\\": \\"replaced text\\" + } + ], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record canvas elements 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 17, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 33 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 9, + \\"id\\": 33, + \\"type\\": 1, + \\"commands\\": [ + { + \\"property\\": \\"createProgram\\", + \\"args\\": [] + }, + { + \\"property\\": \\"linkProgram\\", + \\"args\\": [ + { + \\"rr_type\\": \\"WebGLProgram\\", + \\"index\\": 0 + } + ] + }, + { + \\"property\\": \\"clear\\", + \\"args\\": [ + 16384 + ] + } + ] + } + } +]" +`; + +exports[`cross origin iframes move-node.html should record custom events 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/move-node.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 12 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 16 + } + ], + \\"id\\": 15 + } + ], + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 18 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 21 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 22 + } + ], + \\"id\\": 19 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 25 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"i\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"b\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 29 + } + ], + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 30 + } + ], + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + } + ], + \\"id\\": 24 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"id\\": 32 + } + ], + \\"id\\": 17 + } + ], + \\"id\\": 13 + } + ], + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 5, + \\"data\\": { + \\"tag\\": \\"test\\", + \\"payload\\": { + \\"id\\": 11, + \\"parentId\\": 11, + \\"nextId\\": 12 + } + } + } +]" +`; + +exports[`same origin iframes should emit contents of iframe once 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 14 + } + ], + \\"rootId\\": 11, + \\"id\\": 12 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 13, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 15 + } + }, + { + \\"parentId\\": 15, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, + \\"id\\": 16 + } + } + ] + } + } +]" +`; diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts new file mode 100644 index 0000000000..b580ba8a61 --- /dev/null +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -0,0 +1,514 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import type * as puppeteer from 'puppeteer'; +import type { recordOptions } from '../../src/types'; +import type { + listenerHandler, + eventWithTime, + mutationData, +} from '@rrweb/types'; +import { EventType, IncrementalSource } from '@rrweb/types'; +import { + assertSnapshot, + getServerURL, + launchPuppeteer, + startServer, + stripBase64, + waitForRAF, +} from '../utils'; +import type * as http from 'http'; + +interface ISuite { + code: string; + browser: puppeteer.Browser; + page: puppeteer.Page; + events: eventWithTime[]; + server: http.Server; + serverURL: string; +} + +interface IWindow extends Window { + rrweb: { + record: ( + options: recordOptions, + ) => listenerHandler | undefined; + addCustomEvent(tag: string, payload: T): void; + }; + emit: (e: eventWithTime) => undefined; + snapshots: eventWithTime[]; +} + +async function injectRecordScript(frame: puppeteer.Frame) { + await frame.addScriptTag({ + path: path.resolve(__dirname, '../../dist/rrweb.js'), + }); + await frame.evaluate(() => { + ((window as unknown) as IWindow).snapshots = []; + const { record } = ((window as unknown) as IWindow).rrweb; + record({ + recordCrossOriginIframes: true, + recordCanvas: true, + emit(event) { + ((window as unknown) as IWindow).snapshots.push(event); + ((window as unknown) as IWindow).emit(event); + }, + }); + }); + + for (const child of frame.childFrames()) { + await injectRecordScript(child); + } +} + +const setup = function (this: ISuite, content: string): ISuite { + const ctx = {} as ISuite; + + beforeAll(async () => { + ctx.browser = await launchPuppeteer(); + ctx.server = await startServer(); + ctx.serverURL = getServerURL(ctx.server); + // ctx.serverB = await startServer(); + // ctx.serverBURL = getServerURL(ctx.serverB); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.js'); + ctx.code = fs.readFileSync(bundlePath, 'utf8'); + }); + + beforeEach(async () => { + ctx.page = await ctx.browser.newPage(); + await ctx.page.goto('about:blank'); + await ctx.page.setContent( + content.replace(/\{SERVER_URL\}/g, ctx.serverURL), + ); + // await ctx.page.evaluate(ctx.code); + ctx.events = []; + await ctx.page.exposeFunction('emit', (e: eventWithTime) => { + if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) { + return; + } + ctx.events.push(e); + }); + + ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + await injectRecordScript(ctx.page.mainFrame()); + }); + + afterEach(async () => { + await ctx.page.close(); + }); + + afterAll(async () => { + await ctx.browser.close(); + ctx.server.close(); + // ctx.serverB.close(); + }); + + return ctx; +}; + +describe('cross origin iframes', function (this: ISuite) { + jest.setTimeout(100_000); + + describe('form.html', function (this: ISuite) { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + ); + + it("won't emit events if it isn't in the top level iframe", async () => { + const el = (await ctx.page.$( + 'body > iframe', + )) as puppeteer.ElementHandle; + + const frame = await el.contentFrame(); + const events = await frame?.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + expect(events).toMatchObject([]); + }); + + it('will emit events if it is in the top level iframe', async () => { + const events = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + expect(events.length).not.toBe(0); + }); + + it('should emit contents of iframe', async () => { + const events = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + await waitForRAF(ctx.page); + // two events (full snapshot + meta) from main frame, and one full snapshot from iframe + expect(events.length).toBe(3); + }); + + it('should emit full snapshot event from iframe as mutation event', async () => { + const events = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + await waitForRAF(ctx.page); + // two events from main frame, and two from iframe + expect(events[events.length - 1]).toMatchObject({ + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + adds: [ + { + parentId: expect.any(Number), + node: { + id: expect.any(Number), + }, + }, + ], + }, + }); + }); + + it('should use unique id for child of iframes', async () => { + const events: eventWithTime[] = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + await waitForRAF(ctx.page); + expect( + (events[events.length - 1].data as mutationData).adds[0].node.id, + ).not.toBe(1); + }); + + it('should replace the existing DOM nodes on iframe navigation with `isAttachIframe`', async () => { + await ctx.page.evaluate((url) => { + const iframe = document.querySelector('iframe') as HTMLIFrameElement; + iframe.src = `${url}/html/form.html?2`; + }, ctx.serverURL); + await waitForRAF(ctx.page); // loads iframe + + await injectRecordScript(ctx.page.mainFrame().childFrames()[0]); // injects script into new iframe + + const events: eventWithTime[] = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + expect( + (events[events.length - 1].data as mutationData).removes, + ).toMatchObject([]); + expect( + (events[events.length - 1].data as mutationData).isAttachIframe, + ).toBeTruthy(); + }); + + it('should map input events correctly', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.type('input[type="text"]', 'test'); + await frame.click('input[type="radio"]'); + await frame.click('input[type="checkbox"]'); + await frame.type('input[type="password"]', 'password'); + await frame.type('textarea', 'textarea test'); + await frame.select('select', '1'); + + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should map scroll events correctly', async () => { + // force scrollbars in iframe + ctx.page.evaluate(() => { + const iframe = document.querySelector('iframe') as HTMLIFrameElement; + iframe.style.width = '50px'; + iframe.style.height = '50px'; + }); + + await waitForRAF(ctx.page); + const frame = ctx.page.mainFrame().childFrames()[0]; + + // scroll a little + frame.evaluate(() => { + window.scrollTo(0, 10); + }); + await waitForRAF(ctx.page); + + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + }); + + describe('move-node.html', function (this: ISuite) { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + ); + + it('should record DOM node movement', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const div = document.createElement('div'); + const span = document.querySelector('span')!; + document.body.appendChild(div); + div.appendChild(span); + }); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record DOM node removal', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const span = document.querySelector('span')!; + span.remove(); + }); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record DOM attribute changes', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const span = document.querySelector('span')!; + span.className = 'added-class-name'; + }); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record DOM text changes', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const b = document.querySelector('b')!; + b.childNodes[0].textContent = 'replaced text'; + }); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record canvas elements', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + var canvas = document.createElement('canvas'); + var gl = canvas.getContext('webgl')!; + var program = gl.createProgram()!; + gl.linkProgram(program); + gl.clear(gl.COLOR_BUFFER_BIT); + document.body.appendChild(canvas); + }); + await waitForRAF(ctx.page); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record custom events', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + ((window as unknown) as IWindow).rrweb.addCustomEvent('test', { + id: 1, + parentId: 1, + nextId: 2, + }); + }); + await waitForRAF(ctx.page); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('captures mutations on adopted stylesheets', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await ctx.page.evaluate(() => { + const sheet = new CSSStyleSheet(); + // Add stylesheet to a document. + document.adoptedStyleSheets = [sheet]; + }); + await frame.evaluate(() => { + const sheet = new CSSStyleSheet(); + // Add stylesheet to a document. + document.adoptedStyleSheets = [sheet]; + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + document.adoptedStyleSheets![0].replace!('div { color: yellow; }'); + }); + await frame.evaluate(() => { + document.adoptedStyleSheets![0].replace!('h1 { color: blue; }'); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + document.adoptedStyleSheets![0].replaceSync!( + 'div { display: inline ; }', + ); + }); + await frame.evaluate(() => { + document.adoptedStyleSheets![0].replaceSync!( + 'h1 { font-size: large; }', + ); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + (document.adoptedStyleSheets![0] + .cssRules[0] as CSSStyleRule).style.setProperty('color', 'green'); + (document.adoptedStyleSheets![0] + .cssRules[0] as CSSStyleRule).style.removeProperty('display'); + }); + await frame.evaluate(() => { + (document.adoptedStyleSheets![0] + .cssRules[0] as CSSStyleRule).style.setProperty( + 'font-size', + 'medium', + 'important', + ); + document.adoptedStyleSheets![0].insertRule('h2 { color: red; }'); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + document.adoptedStyleSheets![0].insertRule( + 'body { border: 2px solid blue; }', + 1, + ); + }); + await frame.evaluate(() => { + document.adoptedStyleSheets![0].deleteRule(0); + }); + await waitForRAF(ctx.page); + + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('captures mutations on stylesheets', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await ctx.page.evaluate(() => { + // Add stylesheet to a document. + const style = document.createElement('style'); + document.head.appendChild(style); + }); + await frame.evaluate(() => { + // Add stylesheet to a document. + const style = document.createElement('style'); + document.head.appendChild(style); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + document.styleSheets[0].insertRule('div { color: yellow; }'); + }); + await frame.evaluate(() => { + document.styleSheets[0].insertRule('h1 { color: blue; }'); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + (document.styleSheets[0].cssRules[0] as CSSStyleRule).style.setProperty( + 'color', + 'green', + ); + (document.styleSheets[0] + .cssRules[0] as CSSStyleRule).style.removeProperty('display'); + }); + await frame.evaluate(() => { + (document.styleSheets[0].cssRules[0] as CSSStyleRule).style.setProperty( + 'font-size', + 'medium', + 'important', + ); + document.styleSheets[0].insertRule('h2 { color: red; }'); + }); + await waitForRAF(ctx.page); + await ctx.page.evaluate(() => { + document.styleSheets[0].insertRule( + 'body { border: 2px solid blue; }', + 1, + ); + }); + await frame.evaluate(() => { + document.styleSheets[0].deleteRule(0); + }); + await waitForRAF(ctx.page); + + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + }); + + describe('audio.html', function (this: ISuite) { + jest.setTimeout(100_000); + + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + ); + + it('should emit contents of iframe once', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const el = document.querySelector('audio')!; + el.play(); + }); + await waitForRAF(ctx.page); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + }); +}); + +describe('same origin iframes', function (this: ISuite) { + jest.setTimeout(100_000); + + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + ); + + it('should emit contents of iframe once', async () => { + const events = await ctx.page.evaluate( + () => ((window as unknown) as IWindow).snapshots, + ); + await waitForRAF(ctx.page); + // two events (full snapshot + meta) from main frame, + // and two (full snapshot + mutation) from iframe + expect(events.length).toBe(4); + assertSnapshot(events); + }); +}); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index e269c02a78..b1c799498d 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -191,6 +191,11 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { }), null, 2, + ).replace( + // servers might get run on a random port, + // so we need to normalize the port number + /http:\/\/localhost:\d+/g, + 'http://localhost:3030', ); } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index cd71cac6d1..d2ed8ded5b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -216,6 +216,23 @@ export type SamplingStrategy = Partial<{ canvas: 'all' | number; }>; +export interface ICrossOriginIframeMirror { + getId( + iframe: HTMLIFrameElement, + remoteId: number, + parentToRemoteMap?: Map, + remoteToParentMap?: Map, + ): number; + getIds(iframe: HTMLIFrameElement, remoteId: number[]): number[]; + getRemoteId( + iframe: HTMLIFrameElement, + parentId: number, + map?: Map, + ): number; + getRemoteIds(iframe: HTMLIFrameElement, parentId: number[]): number[]; + reset(iframe?: HTMLIFrameElement): void; +} + export type RecordPlugin = { name: string; observer?: ( @@ -224,7 +241,11 @@ export type RecordPlugin = { options: TOptions, ) => listenerHandler; eventProcessor?: (event: eventWithTime) => eventWithTime & TExtend; - getMirror?: (mirror: Mirror) => void; + getMirror?: (mirrors: { + nodeMirror: Mirror; + crossOriginIframeMirror: ICrossOriginIframeMirror; + crossOriginIframeStyleMirror: ICrossOriginIframeMirror; + }) => void; options: TOptions; }; @@ -636,3 +657,16 @@ declare global { export type IWindow = Window & typeof globalThis; export type Optional = Pick, K> & Omit; + +export type GetTypedKeys = TakeTypeHelper< + Obj, + ValueType +>[keyof TakeTypeHelper]; +export type TakeTypeHelper = { + [K in keyof Obj]: Obj[K] extends ValueType ? K : never; +}; + +export type TakeTypedKeyValues = Pick< + Obj, + TakeTypeHelper[keyof TakeTypeHelper] +>; diff --git a/yarn.lock b/yarn.lock index 155fc700e0..232a43dc6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2188,10 +2188,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest-image-snapshot@^4.3.1": - version "4.3.1" - resolved "https://registry.npmjs.org/@types/jest-image-snapshot/-/jest-image-snapshot-4.3.1.tgz" - integrity sha512-WDdUruGF14C53axe/mNDgQP2YIhtcwXrwmmVP8eOGyfNTVD+FbxWjWR7RTU+lzEy4K6V6+z7nkVDm/auI/r3xQ== +"@types/jest-image-snapshot@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/jest-image-snapshot/-/jest-image-snapshot-5.1.0.tgz#aa355ec40625fcb338fd31c935791bc8fde72bcf" + integrity sha512-pfCz6dclA8mDxwXN/x/PuYBCPwzGuYcTfOVZvDAikC1GLXg/CwECF9UgtWse0tR42c6OaL7LPEnN7i/Dm86KkQ== dependencies: "@types/jest" "*" "@types/pixelmatch" "*" @@ -4159,7 +4159,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: +debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -4287,6 +4287,11 @@ devtools-protocol@0.0.869402: resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" integrity sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA== +devtools-protocol@0.0.901419: + version "0.0.901419" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" + integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz" @@ -4909,10 +4914,10 @@ eslint-plugin-compat@^4.0.2: lodash.memoize "4.1.2" semver "7.3.5" -eslint-plugin-jest@^26.5.3: - version "26.5.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" - integrity sha512-sICclUqJQnR1bFRZGLN2jnSVsYOsmPYYnroGCIMVSvTS3y8XR3yjzy1EcTQmk6typ5pRgyIWzbjqxK6cZHEZuQ== +eslint-plugin-jest@^27.1.3: + version "27.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.1.3.tgz#9f359eeac0c720a825f658e7e261a9eef869dc8d" + integrity sha512-7DrIfYRQPa7JQd1Le8G/BJsfYHVUKQdJQ/6vULSp/4NjKZmSMJ/605G2hhScEra++SiH68zPEjLnrO74nHrMLg== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -5225,17 +5230,7 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -extract-zip@^1.6.6: - version "1.7.0" - resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -extract-zip@^2.0.0: +extract-zip@2.0.1, extract-zip@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -5246,6 +5241,16 @@ extract-zip@^2.0.0: optionalDependencies: "@types/yauzl" "^2.9.1" +extract-zip@^1.6.6: + version "1.7.0" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" @@ -6066,6 +6071,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz" @@ -6074,14 +6087,6 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" @@ -7090,10 +7095,10 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-image-snapshot@^4.5.1: - version "4.5.1" - resolved "https://registry.npmjs.org/jest-image-snapshot/-/jest-image-snapshot-4.5.1.tgz" - integrity sha512-0YkgupgkkCx0wIZkxvqs/oNiUT0X0d2WTpUhaAp+Dy6CpqBUZMRTIZo4KR1f+dqmx6WXrLCvecjnHLIsLkI+gQ== +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" + integrity sha512-msKQqsxr4ZS8S3FQ6ot1SPlDKc4pCfyKY3SxU9LEoASj1zoEfglDYjmxNX53pxpNf7Fp7CJZvwP4xkNXVQgEXA== dependencies: chalk "^1.1.3" get-stdin "^5.0.1" @@ -8575,6 +8580,13 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-fetch@2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" + integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + dependencies: + whatwg-url "^5.0.0" + node-fetch@^2.6.1: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" @@ -9331,6 +9343,13 @@ pixelmatch@^5.1.0: dependencies: pngjs "^4.0.1" +pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + "pkg-dir@< 6 >= 5": version "5.0.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz" @@ -9338,13 +9357,6 @@ pixelmatch@^5.1.0: dependencies: find-up "^5.0.0" -pkg-dir@^4.1.0, pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pngjs@^3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz" @@ -9790,7 +9802,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.3, progress@^2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -9851,7 +9863,7 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@1.1.0, proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -9888,6 +9900,24 @@ puppeteer@^1.15.0: rimraf "^2.6.1" ws "^6.1.0" +puppeteer@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-11.0.0.tgz#0808719c38e15315ecc1b1c28911f1c9054d201f" + integrity sha512-6rPFqN1ABjn4shgOICGDBITTRV09EjXVqhDERBDKwCLz0UyBxeeBH6Ay0vQUJ84VACmlxwzOIzVEJXThcF3aNg== + dependencies: + debug "4.3.2" + devtools-protocol "0.0.901419" + extract-zip "2.0.1" + https-proxy-agent "5.0.0" + node-fetch "2.6.5" + pkg-dir "4.2.0" + progress "2.0.3" + proxy-from-env "1.1.0" + rimraf "3.0.2" + tar-fs "2.1.1" + unbzip2-stream "1.4.3" + ws "8.2.3" + puppeteer@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" @@ -10298,6 +10328,13 @@ rgba-regex@^1.0.0: resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" @@ -10305,13 +10342,6 @@ rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup-plugin-css-only@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz" @@ -11147,7 +11177,7 @@ 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== -tar-fs@^2.0.0: +tar-fs@2.1.1, tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -11677,7 +11707,7 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" -unbzip2-stream@^1.3.3: +unbzip2-stream@1.4.3, unbzip2-stream@^1.3.3: version "1.4.3" resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== @@ -12078,6 +12108,11 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +ws@8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + ws@^6.1.0: version "6.2.2" resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz" From 7bb68625e3ff39258a8cc8614f0691f265cb5bee Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 16 Nov 2022 13:12:31 +0800 Subject: [PATCH 091/201] chore(release): publish new version - rrdom-nodejs@0.1.7 - rrdom@0.1.7 - rrweb-player@1.0.0-alpha.4 - rrweb-snapshot@2.0.0-alpha.4 - rrweb@2.0.0-alpha.4 - @rrweb/types@2.0.0-alpha.4 --- packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/package.json | 6 +++--- packages/rrweb-player/package.json | 6 +++--- packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/package.json | 8 ++++---- packages/types/package.json | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 84bcc4578c..f1be1665aa 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "0.1.6", + "version": "0.1.7", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,8 +49,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^0.1.6", - "rrweb-snapshot": "^2.0.0-alpha.3" + "rrdom": "^0.1.7", + "rrweb-snapshot": "^2.0.0-alpha.4" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 27c1ed1f15..440358d199 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "0.1.6", + "version": "0.1.7", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.js", @@ -31,6 +31,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", + "@rrweb/types": "^2.0.0-alpha.4", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -43,10 +44,9 @@ "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", "ts-jest": "^27.1.3", - "@rrweb/types": "^2.0.0-alpha.3", "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.3" + "rrweb-snapshot": "^2.0.0-alpha.4" } } diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 6c6a21bd72..224dbdd1d9 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "1.0.0-alpha.3", + "version": "1.0.0-alpha.4", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.3", + "@rrweb/types": "^2.0.0-alpha.4", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -25,7 +25,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.3" + "rrweb": "^2.0.0-alpha.4" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 9cc839218b..e16df04739 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 8e09e593b7..7295020de2 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -80,13 +80,13 @@ "typescript": "^4.7.3" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.3", + "@rrweb/types": "^2.0.0-alpha.4", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^0.1.6", - "rrweb-snapshot": "^2.0.0-alpha.3" + "rrdom": "^0.1.7", + "rrweb-snapshot": "^2.0.0-alpha.4" } } diff --git a/packages/types/package.json b/packages/types/package.json index 428e170ed2..d484ab82d6 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "keywords": [ "rrweb", "@rrweb/types" @@ -41,7 +41,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.3" + "rrweb-snapshot": "^2.0.0-alpha.4" }, "browserslist": [ "supports es6-class" From 59a1e4f7d9541e055ee610536f1a796579151426 Mon Sep 17 00:00:00 2001 From: Yanzhen Yu Date: Wed, 16 Nov 2022 14:57:54 +0800 Subject: [PATCH 092/201] fix @rrweb prefix package access --- packages/types/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/types/package.json b/packages/types/package.json index d484ab82d6..a6060cd16f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,9 @@ { "name": "@rrweb/types", "version": "2.0.0-alpha.4", + "publishConfig": { + "access": "public" + }, "keywords": [ "rrweb", "@rrweb/types" From a220835eeb81ca4f294682e060d46c8853720d7f Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Wed, 16 Nov 2022 14:25:36 +0100 Subject: [PATCH 093/201] Fix: Post message can break cross origin iframe recording (#1053) --- packages/rrweb/src/plugins/canvas-webrtc/record/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts b/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts index 106fc6683d..03aad10c9c 100644 --- a/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts +++ b/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts @@ -262,10 +262,12 @@ export class RRWebPluginCanvasWebRTCRecord { } private isCrossOriginIframeMessageEventContent( - event: MessageEvent, + event: MessageEvent, ): event is MessageEvent { return Boolean( - 'type' in event.data && + event.data && + typeof event.data === 'object' && + 'type' in event.data && 'data' in event.data && (event.data as CrossOriginIframeMessageEventContent).type === 'rrweb-canvas-webrtc' && From 7e8dcdb11dc5dfefcdd19ff5e13ec9d8b5c24dcc Mon Sep 17 00:00:00 2001 From: Jimmy Liu Date: Fri, 2 Dec 2022 02:16:54 +0000 Subject: [PATCH 094/201] Fix muted false -> true not being set --- packages/rrweb/src/replay/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 7c94456c4f..ca38798632 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1216,13 +1216,13 @@ export class Replayer { } const mediaEl = target as HTMLMediaElement | 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) { From b655361a5f0d50a053fcd0e5c823b8494c33b89c Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 6 Jan 2023 11:50:06 +0100 Subject: [PATCH 095/201] Export `recordOptions` type (#1067) --- packages/rrweb/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index 5b7f4223ef..c8b0940caa 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -10,6 +10,10 @@ export { ReplayerEvents, } from '@rrweb/types'; +export type { + recordOptions, +} from './types'; + const { addCustomEvent } = record; const { freezePage } = record; From 36b44e104b91fc74c3e69684111240cd23105340 Mon Sep 17 00:00:00 2001 From: fukang wang Date: Mon, 9 Jan 2023 09:35:54 +0800 Subject: [PATCH 096/201] fix: Failed to execute insertBefore on Node (#1042) Co-authored-by: wangfukang --- packages/rrweb/src/replay/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index ca38798632..ce4dc6d8cb 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -764,6 +764,7 @@ export class Replayer { } }; + this.mirror.reset(); rebuild(event.data.node, { doc: this.iframe.contentDocument, afterAppend, From 44e92cbff981c36e754dfcb9a184eae9e7292ecf Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 9 Jan 2023 04:44:33 +0100 Subject: [PATCH 097/201] Handle errors when observing iframes (#1058) --- packages/rrweb/src/record/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index e40d971613..42b5d2c87d 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -565,7 +565,12 @@ function record( }; iframeManager.addLoadListener((iframeEl) => { - handlers.push(observe(iframeEl.contentDocument!)); + try { + handlers.push(observe(iframeEl.contentDocument!)); + } catch (error) { + // TODO: handle internal error + console.warn(error); + } }); const init = () => { From 729b8bf38c8c7f2e1b22b4e0f7cab14f0807bc74 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 9 Jan 2023 04:47:21 +0100 Subject: [PATCH 098/201] Fix: Catch iframe manager & fix formatting issues (#1083) * Catch any errors observing iframes * Fix formatting --- packages/rrweb/src/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/rrweb/src/index.ts b/packages/rrweb/src/index.ts index c8b0940caa..ecc1695909 100644 --- a/packages/rrweb/src/index.ts +++ b/packages/rrweb/src/index.ts @@ -10,9 +10,7 @@ export { ReplayerEvents, } from '@rrweb/types'; -export type { - recordOptions, -} from './types'; +export type { recordOptions } from './types'; const { addCustomEvent } = record; const { freezePage } = record; From 03821d9b9fa0513e6e373881d43102ceb9388340 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Mon, 9 Jan 2023 06:59:24 +0000 Subject: [PATCH 099/201] harmonize on a single getWindowScroll (#1054) * Fix issue where only indication I could see in any attribute that the document was scrolling was on `window.pageYOffset`, so we hadn't been able to replay scrolling * Apply formatting changes * Update observer.ts help you fix typescript error * Update utils.ts help you fix typescript error Co-authored-by: eoghanmurray Co-authored-by: Yun Feng --- packages/rrweb/src/record/index.ts | 18 ++---------------- packages/rrweb/src/record/observer.ts | 9 +++++---- packages/rrweb/src/utils.ts | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 42b5d2c87d..6228d54217 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -9,6 +9,7 @@ import { on, getWindowWidth, getWindowHeight, + getWindowScroll, polyfill, hasShadowRoot, isSerializedIframe, @@ -379,22 +380,7 @@ function record( type: EventType.FullSnapshot, data: { node, - initialOffset: { - left: - window.pageXOffset !== undefined - ? window.pageXOffset - : document?.documentElement.scrollLeft || - document?.body?.parentElement?.scrollLeft || - document?.body?.scrollLeft || - 0, - top: - window.pageYOffset !== undefined - ? window.pageYOffset - : document?.documentElement.scrollTop || - document?.body?.parentElement?.scrollTop || - document?.body?.scrollTop || - 0, - }, + initialOffset: getWindowScroll(window), }, }), ); diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 975f8fa386..f4989f1011 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -4,6 +4,7 @@ import { throttle, on, hookSetter, + getWindowScroll, getWindowHeight, getWindowWidth, isBlocked, @@ -279,12 +280,12 @@ export function initScrollObserver({ return; } const id = mirror.getId(target as Node); - if (target === doc) { - const scrollEl = (doc.scrollingElement || doc.documentElement)!; + if (target === doc && doc.defaultView) { + const scrollLeftTop = getWindowScroll(doc.defaultView); scrollCb({ id, - x: scrollEl.scrollLeft, - y: scrollEl.scrollTop, + x: scrollLeftTop.left, + y: scrollLeftTop.top, }); } else { scrollCb({ diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index f52a6bb8ea..6ff10b27df 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -167,6 +167,28 @@ export function patch( } } +export function getWindowScroll(win: Window) { + const doc = win.document; + return { + left: doc.scrollingElement + ? doc.scrollingElement.scrollLeft + : win.pageXOffset !== undefined + ? win.pageXOffset + : doc?.documentElement.scrollLeft || + doc?.body?.parentElement?.scrollLeft || + doc?.body?.scrollLeft || + 0, + top: doc.scrollingElement + ? doc.scrollingElement.scrollTop + : win.pageYOffset !== undefined + ? win.pageYOffset + : doc?.documentElement.scrollTop || + doc?.body?.parentElement?.scrollTop || + doc?.body?.scrollTop || + 0, + }; +} + export function getWindowHeight(): number { return ( window.innerHeight || From cfac5e7c7384f3fb92c97c0c40d78d695b39167e Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 10 Jan 2023 01:30:41 +0100 Subject: [PATCH 100/201] Chore: Make tests less flakey & upgrade puppeteer to rrweb-snapshot test suite to run (#1084) * Upgrade puppeteer to 17.1.3 in rrweb-snapshot * Apply formatting changes * Add ? * Make tests less flakey * Make attribute-setting more explicit * Make test less flakey * Make test less flakey * Upgrade puppeteer for rrdom * Use wait for request animation frame instead of timeout * Force append to happen in second event * Wait till iframe was loaded (now 100ms) * Round the currentTime to 1 decimal place --- README.md | 2 +- packages/rrdom/package.json | 2 +- packages/rrweb-snapshot/package.json | 2 +- .../__snapshots__/integration.test.ts.snap | 18 +-- .../test/html/picture-blob.html | 2 +- .../rrweb-snapshot/test/integration.test.ts | 42 +++--- packages/rrweb/test/html/frame2.html | 2 +- packages/rrweb/test/integration.test.ts | 2 +- packages/rrweb/test/replayer.test.ts | 29 +++- packages/rrweb/test/utils.ts | 8 ++ yarn.lock | 134 +++++++----------- 11 files changed, 118 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 3e6490f17b..90c8c71811 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE -## Who's using rrweb +## Who's using rrweb? diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 440358d199..8048c9e072 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -38,7 +38,7 @@ "@typescript-eslint/parser": "^5.23.0", "eslint": "^8.15.0", "jest": "^27.5.1", - "puppeteer": "^9.1.1", + "puppeteer": "^17.1.3", "rollup": "^2.56.3", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index e16df04739..44b4d3e544 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -49,7 +49,7 @@ "jest": "^27.2.4", "jest-snapshot": "^23.6.0", "jsdom": "^16.4.0", - "puppeteer": "^1.15.0", + "puppeteer": "^17.1.3", "rollup": "^2.45.2", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 29acb2b993..448823cbce 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -189,7 +189,7 @@ exports[`integration tests [html file]: block-element.html 1`] = ` `; exports[`integration tests [html file]: compat-mode.html 1`] = ` -" +"Compat Mode; image resizing @@ -287,12 +287,12 @@ exports[`integration tests [html file]: iframe.html 1`] = ` `; exports[`integration tests [html file]: iframe-inner.html 1`] = ` -" +"" `; exports[`integration tests [html file]: invalid-attribute.html 1`] = ` -" +"" `; @@ -335,7 +335,7 @@ exports[`integration tests [html file]: mask-text.html 1`] = ` `; exports[`integration tests [html file]: picture.html 1`] = ` -" +" @@ -345,20 +345,20 @@ exports[`integration tests [html file]: picture.html 1`] = ` `; exports[`integration tests [html file]: picture-blob.html 1`] = ` -" - \\"This +" + \\"This " `; exports[`integration tests [html file]: picture-blob-in-frame.html 1`] = ` -" +" " `; exports[`integration tests [html file]: picture-in-frame.html 1`] = ` -" +" " `; @@ -750,7 +750,7 @@ exports[`shadow DOM integration tests snapshot shadow DOM 1`] = ` \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px/22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\", + \\"textContent\\": \\":host { display: inline-block; width: 650px; font-family: \\\\\\"Roboto Slab\\\\\\"; contain: content; }:host([background]) { background: var(--background-color, #9E9E9E); border-radius: 10px; padding: 10px; }#panels { box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 2px; background: white; border-radius: 3px; padding: 16px; height: 250px; overflow: auto; }#tabs { display: inline-flex; user-select: none; }#tabs slot { display: inline-flex; }#tabs ::slotted(*) { font: 400 16px / 22px Roboto; padding: 16px 8px; margin: 0px; text-align: center; width: 100px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; cursor: pointer; border-top-left-radius: 3px; border-top-right-radius: 3px; background: linear-gradient(rgb(250, 250, 250), rgb(238, 238, 238)); border: none; }#tabs ::slotted([aria-selected=\\\\\\"true\\\\\\"]) { font-weight: 600; background: white; box-shadow: none; }#tabs ::slotted(:focus) { z-index: 1; }#panels ::slotted([aria-hidden=\\\\\\"true\\\\\\"]) { display: none; }\\", \\"isStyle\\": true, \\"id\\": 38 } diff --git a/packages/rrweb-snapshot/test/html/picture-blob.html b/packages/rrweb-snapshot/test/html/picture-blob.html index d2a32658a1..8e18b0cf2f 100644 --- a/packages/rrweb-snapshot/test/html/picture-blob.html +++ b/packages/rrweb-snapshot/test/html/picture-blob.html @@ -9,7 +9,7 @@ const robotBlobUrl = URL.createObjectURL(robotBlob); const images = document.querySelectorAll('img'); images.forEach((img) => { - img.src = robotBlobUrl; + img.setAttribute('src', robotBlobUrl); }); }, 0); diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index a9e0a34d68..ab31b58c4a 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -128,8 +128,8 @@ describe('integration tests', function (this: ISuite) { waitUntil: 'load', }); } - const rebuildHtml = ( - await page.evaluate(`${code} + await waitForRAF(page); + const rebuildHtml = ((await page.evaluate(`${code} const x = new XMLSerializer(); const snap = rrweb.snapshot(document); let out = x.serializeToString(rrweb.rebuild(snap, { doc: document })); @@ -138,8 +138,12 @@ describe('integration tests', function (this: ISuite) { out = out.replace(' xmlns=\"http://www.w3.org/1999/xhtml\"', ''); } out; // return - `) - ).replace(/\n\n/g, ''); + `)) as string) + .replace(/\n\n/g, '') + .replace( + /blob:http:\/\/localhost:\d+\/[0-9a-z\-]+/, + 'blob:http://localhost:xxxx/...', + ); expect(rebuildHtml).toMatchSnapshot(); }); } @@ -158,9 +162,9 @@ describe('integration tests', function (this: ISuite) { compatMode + ' for compat-mode.html should be BackCompat as DOCTYPE is deliberately omitted', ); - const renderedHeight = await page.evaluate( + const renderedHeight = (await page.evaluate( 'document.querySelector("center").clientHeight', - ); + )) as number; // can remove following assertion if dimensions of page change assert( renderedHeight < 400, @@ -203,8 +207,10 @@ iframe.contentDocument.querySelector('center').clientHeight inlineImages: true, inlineStylesheet: false })`); - await page.waitFor(100); - const snapshot = await page.evaluate('JSON.stringify(snapshot, null, 2);'); + await waitForRAF(page); + const snapshot = (await page.evaluate( + 'JSON.stringify(snapshot, null, 2);', + )) as string; assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); @@ -221,8 +227,10 @@ iframe.contentDocument.querySelector('center').clientHeight inlineImages: true, inlineStylesheet: false })`); - await page.waitFor(100); - const snapshot = await page.evaluate('JSON.stringify(snapshot, null, 2);'); + await waitForRAF(page); + const snapshot = (await page.evaluate( + 'JSON.stringify(snapshot, null, 2);', + )) as string; assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); @@ -244,10 +252,10 @@ iframe.contentDocument.querySelector('center').clientHeight window.snapshot = sn; } })`); - await page.waitFor(100); - const snapshot = await page.evaluate( + await waitForRAF(page); + const snapshot = (await page.evaluate( 'JSON.stringify(window.snapshot, null, 2);', - ); + )) as string; assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); @@ -269,10 +277,10 @@ iframe.contentDocument.querySelector('center').clientHeight window.snapshot = sn; } })`); - await page.waitFor(100); - const snapshot = await page.evaluate( + await waitForRAF(page); + const snapshot = (await page.evaluate( 'JSON.stringify(window.snapshot, null, 2);', - ); + )) as string; assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); @@ -287,7 +295,7 @@ iframe.contentDocument.querySelector('center').clientHeight window.snapshot = rrweb.snapshot(document, { inlineStylesheet: true, })`); - await page.waitFor(100); + await waitForRAF(page); const snapshot = (await page.evaluate( 'JSON.stringify(window.snapshot, null, 2);', )) as string; diff --git a/packages/rrweb/test/html/frame2.html b/packages/rrweb/test/html/frame2.html index 6344438ca2..205059a52a 100644 --- a/packages/rrweb/test/html/frame2.html +++ b/packages/rrweb/test/html/frame2.html @@ -13,6 +13,6 @@ iframe5.id = 'five'; setTimeout(() => { document.body.appendChild(iframe5); - }, 10); + }, 100); diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 82ce7d1933..87e4fd2016 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -672,7 +672,7 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'frame2.html')); - await page.waitForTimeout(10); // wait till frame was added to dom + await page.waitForSelector('iframe'); // wait for iframe to get added await waitForRAF(page); // wait till browser loaded contents of frame await page.evaluate(() => { diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 7bc09ae2ca..6d94a47cd1 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -798,8 +798,23 @@ describe('replayer', function () { events = ${JSON.stringify(adoptedStyleSheetModification)}; const { Replayer } = rrweb; var replayer = new Replayer(events,{showDebug:true}); - replayer.play(); - `); + replayer.pause(0); + + async function playTill(offsetTime) { + replayer.play(); + return new Promise((resolve) => { + const checkTime = () => { + if (replayer.getCurrentTime() >= offsetTime) { + replayer.pause(); + resolve(undefined); + } else { + requestAnimationFrame(checkTime); + } + }; + checkTime(); + }); + }`); + const iframe = await page.$('iframe'); const contentDocument = await iframe!.contentFrame()!; @@ -916,19 +931,19 @@ describe('replayer', function () { ).toBeTruthy(); }; - await page.waitForTimeout(235); + await page.evaluate(`playTill(250)`); await check250ms(); - await page.waitForTimeout(50); + await page.evaluate(`playTill(300)`); await check300ms(); - await page.waitForTimeout(100); + await page.evaluate(`playTill(400)`); await check400ms(); - await page.waitForTimeout(100); + await page.evaluate(`playTill(500)`); await check500ms(); - await page.waitForTimeout(100); + await page.evaluate(`playTill(600)`); await check600ms(); // To test the correctness of replaying adopted stylesheet mutation events in the fast-forward mode. diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index b1c799498d..d4bd396f63 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -185,6 +185,14 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { } } }); + } else if ( + s.type === EventType.IncrementalSnapshot && + s.data.source === IncrementalSource.MediaInteraction + ) { + // round the currentTime to 1 decimal place + if (s.data.currentTime) { + s.data.currentTime = Math.round(s.data.currentTime * 10) / 10; + } } delete (s as Optional).timestamp; return s as event; diff --git a/yarn.lock b/yarn.lock index 232a43dc6e..f2756537e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2659,13 +2659,6 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - agentkeepalive@^4.1.3: version "4.1.4" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz" @@ -2887,11 +2880,6 @@ ast-metadata-inferer@^0.7.0: dependencies: "@mdn/browser-compat-data" "^3.3.14" -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" @@ -3686,16 +3674,6 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" @@ -3915,6 +3893,13 @@ cross-env@^5.2.0: dependencies: cross-spawn "^6.0.5" +cross-fetch@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" @@ -4152,7 +4137,7 @@ dateformat@^3.0.0: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2.6.9, debug@^2.6.9: +debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -4166,14 +4151,7 @@ debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: dependencies: ms "2.1.2" -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.3.3, debug@^4.3.4: +debug@4.3.4, 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== @@ -4282,6 +4260,11 @@ detect-newline@^3.0.0: resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +devtools-protocol@0.0.1036444: + version "0.0.1036444" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1036444.tgz#a570d3cdde61527c82f9b03919847b8ac7b1c2b9" + integrity sha512-0y4f/T8H9lsESV9kKP1HDUXgHxCdniFeJh6Erq+FbdOEvp/Ydp9t8kcAAM5gOd17pMrTDlFWntoHtzzeTUWKNw== + devtools-protocol@0.0.869402: version "0.0.869402" resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" @@ -4587,18 +4570,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - esbuild-android-64@0.14.38: version "0.14.38" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64" @@ -5241,16 +5212,6 @@ extract-zip@2.0.1, extract-zip@^2.0.0: optionalDependencies: "@types/yauzl" "^2.9.1" -extract-zip@^1.6.6: - version "1.7.0" - resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" @@ -6079,13 +6040,13 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== +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== dependencies: - agent-base "^4.3.0" - debug "^3.1.0" + agent-base "6" + debug "4" human-signals@^2.1.0: version "2.1.0" @@ -8351,7 +8312,7 @@ mime@1.6.0: resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3, mime@^2.3.1: +mime@^2.3.1: version "2.5.2" resolved "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== @@ -8497,7 +8458,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -8534,7 +8495,7 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -8587,7 +8548,7 @@ node-fetch@2.6.5: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.1: +node-fetch@2.6.7, node-fetch@^2.6.1: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -9863,7 +9824,7 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@1.1.0, proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@1.1.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -9886,20 +9847,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@^1.15.0: - version "1.20.0" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz" - integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ== - dependencies: - debug "^4.1.0" - extract-zip "^1.6.6" - https-proxy-agent "^2.2.1" - mime "^2.0.3" - progress "^2.0.1" - proxy-from-env "^1.0.0" - rimraf "^2.6.1" - ws "^6.1.0" - puppeteer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-11.0.0.tgz#0808719c38e15315ecc1b1c28911f1c9054d201f" @@ -9918,6 +9865,23 @@ puppeteer@^11.0.0: unbzip2-stream "1.4.3" ws "8.2.3" +puppeteer@^17.1.3: + version "17.1.3" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-17.1.3.tgz#2814cf221925e19c681c69aa97401a68b30240c9" + integrity sha512-tVtvNSOOqlq75rUgwLeDAEQoLIiBqmRg0/zedpI6fuqIocIkuxG23A7FIl1oVSkuSMMLgcOP5kVhNETmsmjvPw== + dependencies: + cross-fetch "3.1.5" + debug "4.3.4" + devtools-protocol "0.0.1036444" + extract-zip "2.0.1" + https-proxy-agent "5.0.1" + progress "2.0.3" + proxy-from-env "1.1.0" + rimraf "3.0.2" + tar-fs "2.1.1" + unbzip2-stream "1.4.3" + ws "8.8.1" + puppeteer@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" @@ -10115,7 +10079,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: +readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10335,7 +10299,7 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12113,12 +12077,10 @@ ws@8.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== -ws@^6.1.0: - version "6.2.2" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" +ws@8.8.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== ws@^7.2.3: version "7.5.7" From a596a1ea49f9f025fb70652a419319f3439e97c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:04:12 +0800 Subject: [PATCH 101/201] chore(deps): bump qs from 6.5.2 to 6.5.3 (#1072) Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3. - [Release notes](https://github.com/ljharb/qs/releases) - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3) --- updated-dependencies: - dependency-name: qs dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f2756537e0..0c0df1aee9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9918,9 +9918,9 @@ qs@^6.9.4: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.2" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^6.13.8: version "6.14.1" From 6be4437905314fae93326d47a8cadda5783b0f8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:08:52 +0800 Subject: [PATCH 102/201] chore(deps): bump express from 4.17.1 to 4.18.2 (#1076) Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.18.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.17.1...4.18.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 383 +++++++++++++++++++++++++++--------------------------- 1 file changed, 191 insertions(+), 192 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0c0df1aee9..d893cff8b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2606,13 +2606,13 @@ abbrev@1: resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" acorn-globals@^6.0.0: version "6.0.0" @@ -2816,8 +2816,8 @@ array-differ@^3.0.0: array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== array-ify@^1.0.0: version "1.0.0" @@ -3082,21 +3082,23 @@ bluebird@^3.5.2: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: - bytes "3.1.0" + bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" @@ -3254,10 +3256,10 @@ byte-size@^7.0.0: resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacache@^15.0.5, cacache@^15.2.0: version "15.2.0" @@ -3728,16 +3730,16 @@ construct-style-sheets-polyfill@^3.1.0: resolved "https://registry.yarnpkg.com/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.1.0.tgz#c490abd79efdb359fafa62ec14ea55232be0eecf" integrity sha512-HBLKP0chz8BAY6rBdzda11c3wAZeCZ+kIG4weVC2NM3AXzxx09nhe8t0SQNdloAvg5GLuHwq/0SPOOSPvtCcKw== -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: - safe-buffer "5.1.2" + safe-buffer "5.2.1" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: @@ -3832,13 +3834,13 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== core-js@^2.4.0: version "2.6.12" @@ -4139,25 +4141,25 @@ dateformat@^3.0.0: debug@2.6.9: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@4.3.4, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, 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== dependencies: ms "2.1.2" +debug@4.3.2: + version "4.3.2" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" @@ -4230,7 +4232,12 @@ delegates@^1.0.0: resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@^1.1.2, depd@~1.1.2: +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -4240,10 +4247,10 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-indent@^5.0.0: version "5.0.0" @@ -4409,8 +4416,8 @@ ecc-jsbn@~0.1.1: ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.3.723: version "1.3.784" @@ -4454,8 +4461,8 @@ emojis-list@^3.0.0: encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encoding@^0.1.12: version "0.1.13" @@ -4831,8 +4838,8 @@ escalade@^3.1.1: escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" @@ -5080,8 +5087,8 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== eventemitter3@^4.0.4: version "4.0.7" @@ -5145,37 +5152,38 @@ expect@^27.5.1: jest-message-util "^27.5.1" express@^4.16.4: - version "4.17.1" - resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.20.1" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.11.0" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -5376,17 +5384,17 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" find-cache-dir@^3.3.2: @@ -5478,13 +5486,13 @@ form-data@~2.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fs-constants@^1.0.0: version "1.0.0" @@ -5594,9 +5602,9 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -5992,27 +6000,16 @@ http-cache-semantics@^4.1.0: resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" http-proxy-agent@^4.0.1: version "4.0.1" @@ -6203,11 +6200,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" @@ -6288,7 +6280,7 @@ ip@^1.1.5: ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-absolute-url@^2.0.0: @@ -8199,8 +8191,8 @@ mdurl@^1.0.1: media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== meow@^7.0.0, meow@^7.1.1: version "7.1.1" @@ -8238,8 +8230,8 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== merge-stream@^2.0.0: version "2.0.0" @@ -8253,8 +8245,8 @@ merge2@^1.3.0, merge2@^1.4.1: methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== micromatch@^2.3.11: version "2.3.11" @@ -8293,7 +8285,12 @@ mime-db@1.49.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== -mime-types@^2.1.12, mime-types@~2.1.24: +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: version "2.1.31" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz" integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== @@ -8307,9 +8304,16 @@ mime-types@~2.1.19: dependencies: mime-db "1.49.0" +mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@1.6.0: version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.3.1: @@ -8482,20 +8486,15 @@ mri@^1.1.0: ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0: +ms@2.1.3, ms@^2.0.0: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -8526,7 +8525,12 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.2, negotiator@^0.6.2: +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +negotiator@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== @@ -8874,10 +8878,10 @@ object.values@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -9159,7 +9163,7 @@ parse5@^7.0.0: parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-browserify@^1.0.1: @@ -9213,8 +9217,8 @@ path-strip-sep@^1.0.10: path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== path-type@^3.0.0: version "3.0.0" @@ -9816,9 +9820,9 @@ protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== -proxy-addr@~2.0.5: +proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -9905,12 +9909,7 @@ q@^1.1.2, q@^1.5.1: resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.7.0: - version "6.7.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@^6.9.4: +qs@6.11.0, qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -9960,16 +9959,16 @@ randombytes@^2.1.0: range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" @@ -10479,16 +10478,16 @@ sade@^1.4.0, sade@^1.7.4: dependencies: mri "^1.1.0" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-identifier@^0.4.1: version "0.4.2" resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" @@ -10496,7 +10495,7 @@ safe-identifier@^0.4.1: "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@~1.2.4: @@ -10549,24 +10548,24 @@ semver@~7.3.0: dependencies: lru-cache "^6.0.0" -send@0.17.1: - version "0.17.1" - resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "2.0.0" mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" + ms "2.1.3" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" serialize-javascript@^4.0.0: version "4.0.0" @@ -10575,25 +10574,25 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.18.0" set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" @@ -10863,10 +10862,10 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== strict-uri-encode@^2.0.0: version "2.0.0" @@ -11304,10 +11303,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== tough-cookie@^4.0.0: version "4.0.0" @@ -11611,9 +11610,9 @@ type-fest@^1.0.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -11720,8 +11719,8 @@ universalify@^2.0.0: unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== unquote@~1.1.1: version "1.1.1" @@ -11792,8 +11791,8 @@ util.promisify@~1.0.0: utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid@^3.3.2: version "3.4.0" @@ -11841,8 +11840,8 @@ validator@^13.7.0: vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== vendors@^1.0.0: version "1.0.4" From 9354636201306ec1557683f066d4b7062b58246b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:09:46 +0800 Subject: [PATCH 103/201] chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#1068) Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d893cff8b8..a0be2e4ce3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4184,9 +4184,9 @@ decimal.js@^10.2.1: integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== 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" - integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== dedent@^0.7.0: version "0.7.0" From 55cc1b34871206fbe097a658fd76a3507f5f0091 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:37:09 +0800 Subject: [PATCH 104/201] chore(deps): bump loader-utils from 1.4.0 to 1.4.2 (#1051) Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.2. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.2) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index a0be2e4ce3..d2f0a1ccaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3051,7 +3051,7 @@ before-after-hook@^2.2.0: big.js@^5.2.2: version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: @@ -4456,7 +4456,7 @@ emoji-regex@^9.2.2: emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: @@ -7755,9 +7755,9 @@ json5@2.x, json5@^2.1.2: minimist "^1.2.5" json5@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -7933,9 +7933,9 @@ load-json-file@^6.2.0: type-fest "^0.6.0" loader-utils@^1.1.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + version "1.4.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -8361,7 +8361,12 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== From fe69bd6456cead304bfc77cf72c9db0f8c030842 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 10 Jan 2023 17:20:03 +0800 Subject: [PATCH 105/201] fix: module error (#1087) * fix: module error refactor all suffix of bundled scripts with commonjs module from 'js' to cjs error: ReferenceError: exports is not defined in ES module scope This file is being treated as an ES module because it has a '.js' file extension and '.../rrweb/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension. * update jest config files --- packages/rrdom-nodejs/package.json | 2 +- packages/rrdom/jest.config.js | 2 +- packages/rrdom/package.json | 3 ++- packages/rrweb-player/package.json | 3 ++- packages/rrweb-snapshot/jest.config.js | 2 +- packages/rrweb-snapshot/package.json | 3 ++- packages/rrweb/package.json | 2 +- packages/rrweb/rollup.config.js | 2 +- 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index f1be1665aa..8e947a6ce0 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -15,7 +15,7 @@ "rrdom-nodejs" ], "license": "MIT", - "main": "lib/rrdom-nodejs.js", + "main": "lib/rrdom-nodejs.cjs", "module": "es/rrdom-nodejs.js", "typings": "es", "files": [ diff --git a/packages/rrdom/jest.config.js b/packages/rrdom/jest.config.js index e86e13bab9..e5841e937d 100644 --- a/packages/rrdom/jest.config.js +++ b/packages/rrdom/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { +export default { preset: 'ts-jest', testEnvironment: 'node', }; diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 8048c9e072..b9e125c10f 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -3,7 +3,7 @@ "version": "0.1.7", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", - "main": "lib/rrdom.js", + "main": "lib/rrdom.cjs", "module": "es/rrdom.js", "typings": "es", "unpkg": "dist/rrdom.js", @@ -13,6 +13,7 @@ "es", "typings" ], + "type": "module", "repository": { "type": "git", "url": "git+https://github.com/rrweb-io/rrweb.git" diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 224dbdd1d9..93368119d2 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -37,7 +37,7 @@ "lint": "yarn eslint src/**/*.ts" }, "description": "rrweb's replayer UI", - "main": "lib/index.js", + "main": "lib/index.cjs", "module": "dist/index.mjs", "unpkg": "dist/index.js", "files": [ @@ -46,6 +46,7 @@ "typings" ], "typings": "typings/index.d.ts", + "type": "module", "repository": { "type": "git", "url": "git+https://github.com/rrweb-io/rrweb.git" diff --git a/packages/rrweb-snapshot/jest.config.js b/packages/rrweb-snapshot/jest.config.js index 46cb05c37d..e6cdf180ee 100644 --- a/packages/rrweb-snapshot/jest.config.js +++ b/packages/rrweb-snapshot/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { +export default { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/**.test.ts'], diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 44b4d3e544..898209c5f5 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -14,6 +14,7 @@ "prepublish": "npm run typings && npm run bundle", "lint": "yarn eslint src" }, + "type": "module", "repository": { "type": "git", "url": "git+https://github.com/rrweb-io/rrweb.git" @@ -23,7 +24,7 @@ "snapshot", "DOM" ], - "main": "lib/rrweb-snapshot.js", + "main": "lib/rrweb-snapshot.cjs", "module": "es/rrweb-snapshot.js", "unpkg": "dist/rrweb-snapshot.js", "typings": "typings/index.d.ts", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 7295020de2..85117bdb56 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -27,7 +27,7 @@ "keywords": [ "rrweb" ], - "main": "lib/rrweb-all.js", + "main": "lib/rrweb-all.cjs", "module": "es/rrweb/packages/rrweb/src/entries/all.js", "unpkg": "dist/rrweb.js", "sideEffects": false, diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index 79f17e03f0..1875ea2064 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -186,7 +186,7 @@ for (const c of baseConfigs) { output: [ { format: 'cjs', - file: c.pathFn('lib/rrweb.js'), + file: c.pathFn('lib/rrweb.cjs'), }, ], }); From d08913d0dc506dbf119e94686fe5f01c415316c9 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 10 Jan 2023 17:21:20 +0800 Subject: [PATCH 106/201] Fix missed adopted style sheets of shadow doms in checkout full snapshot (#1086) * fix: adoptedStyleSheets in shadow doms are missed when a full snapshot is checked out after recording has started * fix: avoid removing monkey patch of all existed shadow doms when take a new full snapshot * Apply formatting changes * Update packages/rrweb/test/record.test.ts Co-authored-by: Justin Halsall * fix typo * update outdated snapshot Co-authored-by: Justin Halsall --- packages/rrweb/src/record/index.ts | 2 + .../rrweb/src/record/shadow-dom-manager.ts | 6 +- .../test/__snapshots__/record.test.ts.snap | 205 ++++++++++++++++++ packages/rrweb/test/record.test.ts | 48 +++- 4 files changed, 252 insertions(+), 9 deletions(-) diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 6228d54217..779177967f 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -335,6 +335,8 @@ function record( // When we take a full snapshot, old tracked StyleSheets need to be removed. stylesheetManager.reset(); + // Old shadow doms cache need to be cleared. + shadowDomManager.clearCache(); mutationBuffers.forEach((buf) => buf.lock()); // don't allow any mirror modifications during snapshotting const node = snapshot(document, { diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index 807f6220c9..a03a0c4232 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -129,8 +129,12 @@ export class ShadowDomManager { } } + public clearCache() { + this.shadowDoms = new WeakSet(); + } + public reset() { this.restorePatches.forEach((restorePatch) => restorePatch()); - this.shadowDoms = new WeakSet(); + this.clearCache(); } } diff --git a/packages/rrweb/test/__snapshots__/record.test.ts.snap b/packages/rrweb/test/__snapshots__/record.test.ts.snap index 06964f5eff..ad9b438600 100644 --- a/packages/rrweb/test/__snapshots__/record.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/record.test.ts.snap @@ -1059,6 +1059,211 @@ exports[`record captures adopted stylesheets in shadow doms and iframe 1`] = ` ]" `; +exports[`record captures adopted stylesheets of shadow doms in checkout full snapshot 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 6 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"shadow-host-1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"entry\\", + \\"id\\": 8 + } + ], + \\"id\\": 7, + \\"isShadowHost\\": true + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 15, + \\"id\\": 7, + \\"styleIds\\": [ + 1 + ], + \\"styles\\": [ + { + \\"styleId\\": 1, + \\"rules\\": [ + { + \\"rule\\": \\"h1 { color: blue; }\\", + \\"index\\": 0 + } + ] + } + ] + } + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 6 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"shadow-host-1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"entry\\", + \\"id\\": 8 + } + ], + \\"id\\": 7, + \\"isShadowHost\\": true + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 15, + \\"id\\": 7, + \\"styleIds\\": [ + 1 + ], + \\"styles\\": [ + { + \\"styleId\\": 1, + \\"rules\\": [ + { + \\"rule\\": \\"h1 { color: blue; }\\", + \\"index\\": 0 + } + ] + } + ] + } + } +]" +`; + exports[`record captures inserted style text nodes correctly 1`] = ` "[ { diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 215067d2da..46ad5e5c7e 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -29,9 +29,12 @@ interface ISuite { interface IWindow extends Window { rrweb: { - record: ( + record: (( options: recordOptions, - ) => listenerHandler | undefined; + ) => listenerHandler | undefined) & { + takeFullSnapshot: (isCheckout?: boolean | undefined) => void; + }; + addCustomEvent(tag: string, payload: T): void; }; emit: (e: eventWithTime) => undefined; @@ -491,9 +494,9 @@ describe('record', function (this: ISuite) { iframe!.contentDocument!.adoptedStyleSheets = [sheet2]; iframe!.contentDocument!.body.innerHTML = '

h1 in iframe

'; - const { record } = ((window as unknown) as IWindow).rrweb; - record({ - emit: ((window as unknown) as IWindow).emit, + const { rrweb, emit } = (window as unknown) as IWindow; + rrweb.record({ + emit, }); setTimeout(() => { @@ -565,9 +568,9 @@ describe('record', function (this: ISuite) { sheet2.replaceSync!('div {font-size: large;}'); shadowHost.shadowRoot!.adoptedStyleSheets = [sheet2]; - const { record } = ((window as unknown) as IWindow).rrweb; - record({ - emit: ((window as unknown) as IWindow).emit, + const { rrweb, emit } = (window as unknown) as IWindow; + rrweb.record({ + emit, }); setTimeout(() => { @@ -586,6 +589,35 @@ describe('record', function (this: ISuite) { assertSnapshot(ctx.events); }); + it('captures adopted stylesheets of shadow doms in checkout full snapshot', async () => { + await ctx.page.evaluate(() => { + return new Promise((resolve) => { + document.body.innerHTML = ` +
entry
+ `; + + let shadowHost = document.querySelector('div')!; + shadowHost!.attachShadow({ mode: 'open' }); + const sheet = new CSSStyleSheet(); + sheet.replaceSync!('h1 {color: blue;}'); + shadowHost.shadowRoot!.adoptedStyleSheets = [sheet]; + + const { rrweb, emit } = (window as unknown) as IWindow; + rrweb.record({ + emit, + }); + + setTimeout(() => { + // When a full snapshot is checked out manually, all adoptedStylesheets should also be captured. + rrweb.record.takeFullSnapshot(true); + resolve(undefined); + }, 10); + }); + }); + await waitForRAF(ctx.page); + assertSnapshot(ctx.events); + }); + it('captures stylesheets in iframes with `blob:` url', async () => { await ctx.page.evaluate(() => { const iframe = document.createElement('iframe'); From 66abe17832dbb23b3948af1c394f9a02caccc17b Mon Sep 17 00:00:00 2001 From: dbseel Date: Tue, 10 Jan 2023 04:23:29 -0500 Subject: [PATCH 107/201] Fix: isBlocked throws on invalid HTML element (#1032) --- packages/rrweb/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 6ff10b27df..28f8586ce8 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -235,7 +235,7 @@ export function isBlocked( if (classMatchesRegex(el, blockClass, checkAncestors)) return true; } if (blockSelector) { - if ((node as HTMLElement).matches(blockSelector)) return true; + if (el.matches(blockSelector)) return true; if (checkAncestors && el.closest(blockSelector) !== null) return true; } return false; From 07aa1b2807da5a9a1db678ebc3ff59320a300d06 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 10 Jan 2023 11:51:24 +0100 Subject: [PATCH 108/201] Fix: shadow dom bugs (#1049) * Add test cases for bugs * Fix shadow dom recording When moving an element containing shadow dom When adding an element to shadow dom before its attached to the dom * Apply formatting changes * Refactor in dom checking code * Nodes don't get processed in more than one mutation buffer * Constrain node mutations to one mutation buffer per request animation frame * Make tests less flaky under heavy load * Apply suggestions from code review * Update packages/rrweb-snapshot/test/rebuild.test.ts * Remove unused nodeSet Co-authored-by: Yun Feng --- packages/rrweb-snapshot/test/rebuild.test.ts | 31 + packages/rrweb/src/record/index.ts | 8 +- packages/rrweb/src/record/mutation.ts | 30 +- packages/rrweb/src/record/observer.ts | 2 + .../src/record/processed-node-manager.ts | 34 + .../rrweb/src/record/shadow-dom-manager.ts | 8 +- packages/rrweb/src/types.ts | 3 + packages/rrweb/src/utils.ts | 27 + .../__snapshots__/integration.test.ts.snap | 601 +++++++++++++++++- packages/rrweb/test/html/blank.html | 3 + packages/rrweb/test/integration.test.ts | 113 ++++ 11 files changed, 831 insertions(+), 29 deletions(-) create mode 100644 packages/rrweb/src/record/processed-node-manager.ts create mode 100644 packages/rrweb/test/html/blank.html diff --git a/packages/rrweb-snapshot/test/rebuild.test.ts b/packages/rrweb-snapshot/test/rebuild.test.ts index 70e86a7142..357cd2fb3c 100644 --- a/packages/rrweb-snapshot/test/rebuild.test.ts +++ b/packages/rrweb-snapshot/test/rebuild.test.ts @@ -47,6 +47,37 @@ describe('rebuild', function () { }); }); + describe('shadowDom', function () { + it('rebuild shadowRoot without siblings', function () { + const node = buildNodeWithSN( + { + id: 1, + tagName: 'div', + type: NodeType.Element, + attributes: {}, + childNodes: [ + { + id: 2, + tagName: 'div', + type: NodeType.Element, + attributes: {}, + childNodes: [], + isShadow: true, + }, + ], + isShadowHost: true, + }, + { + doc: document, + mirror, + hackCss: false, + cache, + }, + ) as HTMLDivElement; + expect(node.shadowRoot?.childNodes.length).toBe(1); + }); + }); + describe('add hover class to hover selector related rules', function () { it('will do nothing to css text without :hover', () => { const cssText = 'body { color: white }'; diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 779177967f..acc42acae4 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -4,7 +4,11 @@ import { SlimDOMOptions, createMirror, } from 'rrweb-snapshot'; -import { initObservers, mutationBuffers } from './observer'; +import { + initObservers, + mutationBuffers, + processedNodeManager, +} from './observer'; import { on, getWindowWidth, @@ -316,6 +320,7 @@ function record( stylesheetManager, canvasManager, keepIframeSrcFn, + processedNodeManager, }, mirror, }); @@ -528,6 +533,7 @@ function record( iframeManager, stylesheetManager, shadowDomManager, + processedNodeManager, canvasManager, ignoreCSSAttributes, plugins: diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 179272c098..bcc316ea1d 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -26,6 +26,7 @@ import { hasShadowRoot, isSerializedIframe, isSerializedStylesheet, + inDom, } from '../utils'; type DoubleLinkedListNode = { @@ -175,6 +176,7 @@ export default class MutationBuffer { private stylesheetManager: observerParam['stylesheetManager']; private shadowDomManager: observerParam['shadowDomManager']; private canvasManager: observerParam['canvasManager']; + private processedNodeManager: observerParam['processedNodeManager']; public init(options: MutationBufferParam) { ([ @@ -198,6 +200,7 @@ export default class MutationBuffer { 'stylesheetManager', 'shadowDomManager', 'canvasManager', + 'processedNodeManager', ] as const).forEach((key) => { // just a type trick, the runtime result is correct this[key] = options[key] as never; @@ -271,19 +274,8 @@ export default class MutationBuffer { (n.getRootNode() as ShadowRoot).host ) shadowHost = (n.getRootNode() as ShadowRoot).host; - // If n is in a nested shadow dom. - let rootShadowHost = shadowHost; - while ( - rootShadowHost?.getRootNode?.()?.nodeType === - Node.DOCUMENT_FRAGMENT_NODE && - (rootShadowHost.getRootNode() as ShadowRoot).host - ) - rootShadowHost = (rootShadowHost.getRootNode() as ShadowRoot).host; - // ensure contains is passed a Node, or it will throw an error - const notInDoc = - !this.doc.contains(n) && - (!rootShadowHost || !this.doc.contains(rootShadowHost)); - if (!n.parentNode || notInDoc) { + + if (!n.parentNode || !inDom(n)) { return; } const parentId = isShadowRoot(n.parentNode) @@ -647,6 +639,9 @@ export default class MutationBuffer { * Make sure you check if `n`'s parent is blocked before calling this function * */ private genAdds = (n: Node, target?: Node) => { + // this node was already recorded in other buffer, ignore it + if (this.processedNodeManager.inOtherBuffer(n, this)) return; + if (this.mirror.hasNode(n)) { if (isIgnored(n, this.mirror)) { return; @@ -666,8 +661,15 @@ export default class MutationBuffer { // if this node is blocked `serializeNode` will turn it into a placeholder element // but we have to remove it's children otherwise they will be added as placeholders too - if (!isBlocked(n, this.blockClass, this.blockSelector, false)) + if (!isBlocked(n, this.blockClass, this.blockSelector, false)) { n.childNodes.forEach((childN) => this.genAdds(childN)); + if (hasShadowRoot(n)) { + n.shadowRoot.childNodes.forEach((childN) => { + this.processedNodeManager.add(childN, this); + this.genAdds(childN, n); + }); + } + } }; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index f4989f1011..cfc17a7e79 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -40,6 +40,7 @@ import { selectionCallback, } from '@rrweb/types'; import MutationBuffer from './mutation'; +import ProcessedNodeManager from './processed-node-manager'; type WindowWithStoredMutationObserver = IWindow & { __rrMutationObserver?: MutationObserver; @@ -51,6 +52,7 @@ type WindowWithAngularZone = IWindow & { }; export const mutationBuffers: MutationBuffer[] = []; +export const processedNodeManager = new ProcessedNodeManager(); const isCSSGroupingRuleSupported = typeof CSSGroupingRule !== 'undefined'; const isCSSMediaRuleSupported = typeof CSSMediaRule !== 'undefined'; diff --git a/packages/rrweb/src/record/processed-node-manager.ts b/packages/rrweb/src/record/processed-node-manager.ts new file mode 100644 index 0000000000..60b939bb12 --- /dev/null +++ b/packages/rrweb/src/record/processed-node-manager.ts @@ -0,0 +1,34 @@ +import type MutationBuffer from './mutation'; + +/** + * Keeps a log of nodes that could show up in multiple mutation buffer but shouldn't be handled twice. + */ +export default class ProcessedNodeManager { + private nodeMap: WeakMap> = new WeakMap(); + + constructor() { + this.periodicallyClear(); + } + + private periodicallyClear() { + requestAnimationFrame(() => { + this.clear(); + this.periodicallyClear(); + }); + } + + public inOtherBuffer(node: Node, thisBuffer: MutationBuffer) { + const buffers = this.nodeMap.get(node); + return ( + buffers && Array.from(buffers).some((buffer) => buffer !== thisBuffer) + ); + } + + public add(node: Node, buffer: MutationBuffer) { + this.nodeMap.set(node, (this.nodeMap.get(node) || new Set()).add(buffer)); + } + + private clear() { + this.nodeMap = new WeakMap(); + } +} diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index a03a0c4232..fd90d23d90 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -9,7 +9,7 @@ import { initScrollObserver, initAdoptedStyleSheetObserver, } from './observer'; -import { patch } from '../utils'; +import { patch, inDom } from '../utils'; import type { Mirror } from 'rrweb-snapshot'; import { isNativeShadowDom } from 'rrweb-snapshot'; @@ -49,7 +49,11 @@ export class ShadowDomManager { function (original: (init: ShadowRootInit) => ShadowRoot) { return function (this: HTMLElement, option: ShadowRootInit) { const shadowRoot = original.call(this, option); - if (this.shadowRoot) + + // For the shadow dom elements in the document, monitor their dom mutations. + // For shadow dom elements that aren't in the document yet, + // we start monitoring them once their shadow dom host is appended to the document. + if (this.shadowRoot && inDom(this)) manager.addShadowRoot(this.shadowRoot, this.ownerDocument); return shadowRoot; }; diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 4548717cd6..b204e97407 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -37,6 +37,7 @@ import type { styleSheetRuleCallback, viewportResizeCallback, } from '@rrweb/types'; +import type ProcessedNodeManager from './record/processed-node-manager'; export type recordOptions = { emit?: (e: T, isCheckout?: boolean) => void; @@ -105,6 +106,7 @@ export type observerParam = { stylesheetManager: StylesheetManager; shadowDomManager: ShadowDomManager; canvasManager: CanvasManager; + processedNodeManager: ProcessedNodeManager; ignoreCSSAttributes: Set; plugins: Array<{ observer: ( @@ -139,6 +141,7 @@ export type MutationBufferParam = Pick< | 'stylesheetManager' | 'shadowDomManager' | 'canvasManager' + | 'processedNodeManager' >; export type ReplayPlugin = { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 28f8586ce8..1d5ec83855 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -518,3 +518,30 @@ export class StyleSheetMirror { return this.id++; } } + +export function getRootShadowHost(n: Node): Node | null { + const shadowHost = (n.getRootNode() as ShadowRoot).host; + // If n is in a nested shadow dom. + let rootShadowHost = shadowHost; + + while ( + rootShadowHost?.getRootNode?.()?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && + (rootShadowHost.getRootNode() as ShadowRoot).host + ) + rootShadowHost = (rootShadowHost.getRootNode() as ShadowRoot).host; + + return rootShadowHost; +} + +export function shadowHostInDom(n: Node): boolean { + const doc = n.ownerDocument; + if (!doc) return false; + const shadowHost = getRootShadowHost(n); + return Boolean(shadowHost && doc.contains(shadowHost)); +} + +export function inDom(n: Node): boolean { + const doc = n.ownerDocument; + if (!doc) return false; + return doc.contains(n) || shadowHostInDom(n); +} diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index f2d86c86bb..631580b1aa 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -11514,6 +11514,358 @@ exports[`record integration tests should record input userTriggered values if us ]" `; +exports[`record integration tests should record moved shadow DOM 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 9, + \\"isShadowHost\\": true + } + }, + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 10, + \\"isShadow\\": true + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 4, + \\"id\\": 9 + } + ], + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 10, + \\"isShadow\\": true + } + }, + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 11 + } + }, + { + \\"parentId\\": 11, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 9, + \\"isShadowHost\\": true + } + } + ] + } + } +]" +`; + +exports[`record integration tests should record moved shadow DOM 2 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"newEl\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + } + }, + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"el\\" + }, + \\"childNodes\\": [], + \\"id\\": 10, + \\"isShadowHost\\": true + } + }, + { + \\"parentId\\": 10, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 11, + \\"isShadow\\": true + } + }, + { + \\"parentId\\": 10, + \\"nextId\\": 11, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 12, + \\"isShadow\\": true + } + }, + { + \\"parentId\\": 12, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 13 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 12, + \\"id\\": 13 + } + ], + \\"adds\\": [ + { + \\"parentId\\": 11, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 13 + } + } + ] + } + } +]" +`; + exports[`record integration tests should record mutations in iframes accross pages 1`] = ` "[ { @@ -12620,6 +12972,242 @@ exports[`record integration tests should record shadow DOM 1`] = ` ]" `; +exports[`record integration tests should record shadow DOM 2 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 9, + \\"isShadowHost\\": true + } + }, + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 10, + \\"isShadow\\": true + } + } + ] + } + } +]" +`; + +exports[`record integration tests should record shadow DOM 3 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 4, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 9, + \\"isShadowHost\\": true + } + }, + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 10, + \\"isShadow\\": true + } + } + ] + } + } +]" +`; + exports[`record integration tests should record shadow doms polyfilled by shadydom 1`] = ` "[ { @@ -13121,18 +13709,7 @@ exports[`record integration tests should record shadow doms polyfilled by synthe \\"id\\": 31, \\"isShadowHost\\": true } - } - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [], - \\"removes\\": [], - \\"adds\\": [ + }, { \\"parentId\\": 31, \\"nextId\\": null, diff --git a/packages/rrweb/test/html/blank.html b/packages/rrweb/test/html/blank.html new file mode 100644 index 0000000000..80e6028fd4 --- /dev/null +++ b/packages/rrweb/test/html/blank.html @@ -0,0 +1,3 @@ + + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 87e4fd2016..516d3a289a 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -667,6 +667,119 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should record shadow DOM 2', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'blank.html')); + await page.evaluate(() => { + return new Promise((resolve) => { + const el = document.createElement('div') as HTMLDivElement; + el.attachShadow({ mode: 'open' }); + (el.shadowRoot as ShadowRoot).appendChild( + document.createElement('input'), + ); + setTimeout(() => { + document.body.append(el); + resolve(null); + }, 10); + }); + }); + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record shadow DOM 3', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'blank.html')); + + await page.evaluate(() => { + const el = document.createElement('div') as HTMLDivElement; + el.attachShadow({ mode: 'open' }); + (el.shadowRoot as ShadowRoot).appendChild( + document.createElement('input'), + ); + document.body.append(el); + }); + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record moved shadow DOM', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'blank.html')); + + await page.evaluate(() => { + return new Promise((resolve) => { + const el = document.createElement('div') as HTMLDivElement; + el.attachShadow({ mode: 'open' }); + (el.shadowRoot as ShadowRoot).appendChild( + document.createElement('input'), + ); + document.body.append(el); + setTimeout(() => { + const newEl = document.createElement('div') as HTMLDivElement; + document.body.append(newEl); + newEl.append(el); + resolve(null); + }, 50); + }); + }); + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('should record moved shadow DOM 2', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'blank.html')); + + await page.evaluate(() => { + const el = document.createElement('div') as HTMLDivElement; + el.id = 'el'; + el.attachShadow({ mode: 'open' }); + (el.shadowRoot as ShadowRoot).appendChild( + document.createElement('input'), + ); + document.body.append(el); + (el.shadowRoot as ShadowRoot).appendChild(document.createElement('span')); + (el.shadowRoot as ShadowRoot).appendChild(document.createElement('p')); + const newEl = document.createElement('div') as HTMLDivElement; + newEl.id = 'newEl'; + document.body.append(newEl); + newEl.append(el); + const input = el.shadowRoot?.children[0] as HTMLInputElement; + const span = el.shadowRoot?.children[1] as HTMLSpanElement; + const p = el.shadowRoot?.children[2] as HTMLParagraphElement; + input.remove(); + span.append(input); + p.append(input); + span.append(input); + setTimeout(() => { + p.append(input); + }, 0); + }); + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + it('should record nested iframes and shadow doms', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From 57a2e140ea419f7790b1672529f21dfe2261b52b Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 13 Jan 2023 06:11:57 +0100 Subject: [PATCH 109/201] feat: Guard against missing `window.CSSStyleSheet` (#1088) --- packages/rrweb/src/record/observer.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index cfc17a7e79..324342e1db 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -538,6 +538,13 @@ function initStyleSheetObserver( { styleSheetRuleCb, mirror, stylesheetManager }: observerParam, { win }: { win: IWindow }, ): listenerHandler { + if (!win.CSSStyleSheet || !win.CSSStyleSheet.prototype) { + // If, for whatever reason, CSSStyleSheet is not available, we skip the observation of stylesheets. + return () => { + // Do nothing + }; + } + // eslint-disable-next-line @typescript-eslint/unbound-method const insertRule = win.CSSStyleSheet.prototype.insertRule; win.CSSStyleSheet.prototype.insertRule = function ( From fc828694099b87b4d811e6b651a7bb4c7499b896 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 16 Jan 2023 15:26:33 +0800 Subject: [PATCH 110/201] Fix cross origin iframe bugs (#1093) * fix: error data while recording some websites are integrated with stripe These websites will usually have an iframe with src "https://js.stripe.com/v3/m-outer-xxx.html" * add test case for the bug * fix: recordCrossOriginIframes: true does not work with pack/unpack fn 1. bugfix 2. add test case 3. add rrweb-all.js to the result of command: bundle:browser 4. make puppeteer headless in CI by default to increase the speed and stability --- .github/workflows/ci-cd.yml | 2 +- packages/rrweb/rollup.config.js | 5 + packages/rrweb/src/record/iframe-manager.ts | 36 +- packages/rrweb/src/record/index.ts | 7 +- packages/rrweb/src/types.ts | 2 + .../cross-origin-iframes.test.ts.snap | 412 ++++++++++++++++++ .../test/record/cross-origin-iframes.test.ts | 104 ++++- packages/rrweb/test/utils.ts | 6 +- 8 files changed, 540 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 4a0355b07a..b94c259fcb 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -30,7 +30,7 @@ jobs: run: yarn turbo run check-types - name: Run tests - run: xvfb-run --server-args="-screen 0 1920x1080x24" yarn test + run: PUPPETEER_HEADLESS=true xvfb-run --server-args="-screen 0 1920x1080x24" yarn test - name: Upload diff images to GitHub uses: actions/upload-artifact@v3 diff --git a/packages/rrweb/rollup.config.js b/packages/rrweb/rollup.config.js index 1875ea2064..beb5fc1ee2 100644 --- a/packages/rrweb/rollup.config.js +++ b/packages/rrweb/rollup.config.js @@ -214,6 +214,11 @@ if (process.env.BROWSER_ONLY) { name: 'rrweb', pathFn: (p) => p, }, + { + input: './src/entries/all.ts', + name: 'rrweb', + pathFn: toAllPath, + }, { input: './src/plugins/console/record/index.ts', name: 'rrwebConsoleRecord', diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 775aa6500d..58e830304b 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -83,24 +83,30 @@ export class IframeManager { ); } private handleMessage(message: MessageEvent | CrossOriginIframeMessageEvent) { - if ((message as CrossOriginIframeMessageEvent).data.type === 'rrweb') { - const iframeSourceWindow = message.source; - if (!iframeSourceWindow) return; + const crossOriginMessageEvent = message as CrossOriginIframeMessageEvent; + if ( + crossOriginMessageEvent.data.type !== 'rrweb' || + // To filter out the rrweb messages which are forwarded by some sites. + crossOriginMessageEvent.origin !== crossOriginMessageEvent.data.origin + ) + return; - const iframeEl = this.crossOriginIframeMap.get(message.source); - if (!iframeEl) return; + const iframeSourceWindow = message.source; + if (!iframeSourceWindow) return; - const transformedEvent = this.transformCrossOriginEvent( - iframeEl, - (message as CrossOriginIframeMessageEvent).data.event, - ); + const iframeEl = this.crossOriginIframeMap.get(message.source); + if (!iframeEl) return; - if (transformedEvent) - this.wrappedEmit( - transformedEvent, - (message as CrossOriginIframeMessageEvent).data.isCheckout, - ); - } + const transformedEvent = this.transformCrossOriginEvent( + iframeEl, + crossOriginMessageEvent.data.event, + ); + + if (transformedEvent) + this.wrappedEmit( + transformedEvent, + crossOriginMessageEvent.data.isCheckout, + ); } private transformCrossOriginEvent( diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index acc42acae4..c28ec28a2c 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -165,7 +165,11 @@ function record( e = plugin.eventProcessor(e); } } - if (packFn) { + if ( + packFn && + // Disable packing events which will be emitted to parent frames. + !passEmitsToParent + ) { e = (packFn(e) as unknown) as eventWithTime; } return (e as unknown) as T; @@ -190,6 +194,7 @@ function record( const message: CrossOriginIframeMessageEventContent = { type: 'rrweb', event: eventProcessor(e), + origin: window.location.origin, isCheckout, }; window.parent.postMessage(message, '*'); diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index b204e97407..fb3a300b40 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -200,6 +200,8 @@ declare global { export type CrossOriginIframeMessageEventContent = { type: 'rrweb'; event: T; + // The origin of the iframe which originally emits this message. It is used to check the integrity of message and to filter out the rrweb messages which are forwarded by some sites. + origin: string; isCheckout?: boolean; }; export type CrossOriginIframeMessageEvent = MessageEvent; diff --git a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap index f1c697d34c..da4ff93d25 100644 --- a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap +++ b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap @@ -307,6 +307,418 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] ]" `; +exports[`cross origin iframes blank.html should filter out forwarded cross origin rrweb messages 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/blank.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 15 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 12 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 18 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 18, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 23 + } + ], + \\"id\\": 22 + } + ], + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 25 + } + ], + \\"id\\": 24 + } + ], + \\"id\\": 20 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 19 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + } +]" +`; + +exports[`cross origin iframes blank.html should support packFn option in record() 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + }, + \\"v\\": \\"v1\\" + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/blank.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + }, + \\"v\\": \\"v1\\" + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 15 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"id\\": 17 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 12 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + }, + \\"v\\": \\"v1\\" + } +]" +`; + exports[`cross origin iframes form.html should map input events correctly 1`] = ` "[ { diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index b580ba8a61..0ad704e92b 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -13,9 +13,9 @@ import { getServerURL, launchPuppeteer, startServer, - stripBase64, waitForRAF, } from '../utils'; +import { unpack } from '../../src/packer/unpack'; import type * as http from 'http'; interface ISuite { @@ -33,42 +33,61 @@ interface IWindow extends Window { options: recordOptions, ) => listenerHandler | undefined; addCustomEvent(tag: string, payload: T): void; + pack: (e: eventWithTime) => string; }; emit: (e: eventWithTime) => undefined; snapshots: eventWithTime[]; } +type ExtraOptions = { + usePackFn?: boolean; +}; -async function injectRecordScript(frame: puppeteer.Frame) { +async function injectRecordScript( + frame: puppeteer.Frame, + options?: ExtraOptions, +) { await frame.addScriptTag({ - path: path.resolve(__dirname, '../../dist/rrweb.js'), + path: path.resolve(__dirname, '../../dist/rrweb-all.js'), }); - await frame.evaluate(() => { + options = options || {}; + await frame.evaluate((options) => { ((window as unknown) as IWindow).snapshots = []; - const { record } = ((window as unknown) as IWindow).rrweb; - record({ + const { record, pack } = ((window as unknown) as IWindow).rrweb; + const config: recordOptions = { recordCrossOriginIframes: true, recordCanvas: true, emit(event) { ((window as unknown) as IWindow).snapshots.push(event); ((window as unknown) as IWindow).emit(event); }, - }); - }); + }; + if (options.usePackFn) { + config.packFn = pack; + } + record(config); + }, options); for (const child of frame.childFrames()) { - await injectRecordScript(child); + await injectRecordScript(child, options); } } -const setup = function (this: ISuite, content: string): ISuite { - const ctx = {} as ISuite; +const setup = function ( + this: ISuite, + content: string, + options?: ExtraOptions, +): ISuite { + const ctx = {} as ISuite & { + serverB: http.Server; + serverBURL: string; + }; beforeAll(async () => { ctx.browser = await launchPuppeteer(); ctx.server = await startServer(); ctx.serverURL = getServerURL(ctx.server); - // ctx.serverB = await startServer(); - // ctx.serverBURL = getServerURL(ctx.serverB); + ctx.serverB = await startServer(); + ctx.serverBURL = getServerURL(ctx.serverB); const bundlePath = path.resolve(__dirname, '../../dist/rrweb.js'); ctx.code = fs.readFileSync(bundlePath, 'utf8'); @@ -90,7 +109,7 @@ const setup = function (this: ISuite, content: string): ISuite { }); ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); - await injectRecordScript(ctx.page.mainFrame()); + await injectRecordScript(ctx.page.mainFrame(), options); }); afterEach(async () => { @@ -100,7 +119,7 @@ const setup = function (this: ISuite, content: string): ISuite { afterAll(async () => { await ctx.browser.close(); ctx.server.close(); - // ctx.serverB.close(); + ctx.serverB.close(); }); return ctx; @@ -484,6 +503,61 @@ describe('cross origin iframes', function (this: ISuite) { assertSnapshot(snapshots); }); }); + + describe('blank.html', function (this: ISuite) { + const content = ` + + + + + + + `; + const ctx = setup.call(this, content) as ISuite & { + serverBURL: string; + }; + + it('should filter out forwarded cross origin rrweb messages', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + const iframe2URL = `${ctx.serverBURL}/html/blank.html`; + await frame.evaluate((iframe2URL) => { + // Add a message proxy to forward messages from child frames to its parent frame. + window.addEventListener('message', (event) => { + if (event.source !== window) + window.parent.postMessage(event.data, '*'); + }); + const iframe2 = document.createElement('iframe'); + iframe2.src = iframe2URL; + document.body.appendChild(iframe2); + }, iframe2URL); + + // Wait for iframe2 to load + await ctx.page.waitForFrame(iframe2URL); + // Record iframe2 + await injectRecordScript(frame.childFrames()[0]); + + await waitForRAF(frame.childFrames()[0]); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + describe('should support packFn option in record()', () => { + const ctx = setup.call(this, content, { usePackFn: true }); + it('', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await waitForRAF(frame); + const packedSnapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as string[]; + const unpackedSnapshots = packedSnapshots.map((packed) => + unpack(packed), + ) as eventWithTime[]; + assertSnapshot(unpackedSnapshots); + }); + }); + }); }); describe('same origin iframes', function (this: ISuite) { diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index d4bd396f63..1d9b7a861e 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -576,8 +576,10 @@ export const polyfillWebGLGlobals = () => { global.WebGL2RenderingContext = WebGL2RenderingContext as any; }; -export async function waitForRAF(page: puppeteer.Page) { - return await page.evaluate(() => { +export async function waitForRAF( + pageOrFrame: puppeteer.Page | puppeteer.Frame, +) { + return await pageOrFrame.evaluate(() => { return new Promise((resolve) => { requestAnimationFrame(() => { requestAnimationFrame(resolve); From 7e002b9bdafb23b494178629689b26fce6e57e33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 10:27:30 +0800 Subject: [PATCH 111/201] chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#1108) Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d2f0a1ccaa..0a13a7eded 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5996,9 +5996,9 @@ htmlparser2@^6.1.0: entities "^2.0.0" http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@2.0.0: version "2.0.0" From a77e30217893e63f8025c73afc3ac1ba294d7761 Mon Sep 17 00:00:00 2001 From: James Berry <69924001+jlalmes@users.noreply.github.com> Date: Mon, 6 Feb 2023 02:28:51 +0000 Subject: [PATCH 112/201] [console-plugin] Feat: Record `unhandledrejection` event (#1104) * feat/console-plugin/add-unhandledrejection-handler * better error * change the window object to the passed in 'win' * empty * improve serialized error messages --------- Co-authored-by: Yun Feng --- .../rrweb/src/plugins/console/record/index.ts | 65 ++++++++++++++----- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 47cd513695..221292c486 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -114,25 +114,54 @@ function initLogObserver( const cancelHandlers: listenerHandler[] = []; // add listener to thrown errors if (logOptions.level.includes('error')) { - if (window) { - const errorHandler = (event: ErrorEvent) => { - const message = event.message, - error = event.error as Error; - const trace: string[] = ErrorStackParser.parse( - error, - ).map((stackFrame: StackFrame) => stackFrame.toString()); - const payload = [stringify(message, logOptions.stringifyOptions)]; - cb({ - level: 'error', - trace, - payload, - }); - }; - window.addEventListener('error', errorHandler); - cancelHandlers.push(() => { - if (window) window.removeEventListener('error', errorHandler); + const errorHandler = (event: ErrorEvent) => { + const message = event.message, + error = event.error as Error; + const trace: string[] = ErrorStackParser.parse( + error, + ).map((stackFrame: StackFrame) => stackFrame.toString()); + const payload = [stringify(message, logOptions.stringifyOptions)]; + cb({ + level: 'error', + trace, + payload, }); - } + }; + win.addEventListener('error', errorHandler); + cancelHandlers.push(() => { + win.removeEventListener('error', errorHandler); + }); + const unhandledrejectionHandler = (event: PromiseRejectionEvent) => { + let error: Error; + let payload: string[]; + if (event.reason instanceof Error) { + error = event.reason; + payload = [ + stringify( + `Uncaught (in promise) ${error.name}: ${error.message}`, + logOptions.stringifyOptions, + ), + ]; + } else { + error = new Error(); + payload = [ + stringify('Uncaught (in promise)', logOptions.stringifyOptions), + stringify(event.reason, logOptions.stringifyOptions), + ]; + } + const trace: string[] = ErrorStackParser.parse( + error, + ).map((stackFrame: StackFrame) => stackFrame.toString()); + cb({ + level: 'error', + trace, + payload, + }); + }; + win.addEventListener('unhandledrejection', unhandledrejectionHandler); + cancelHandlers.push(() => { + win.removeEventListener('unhandledrejection', unhandledrejectionHandler); + }); } for (const levelType of logOptions.level) { cancelHandlers.push(replace(logger, levelType)); From 25a4f5ab6c7311f2e8e5e1a4d232c2820adf910e Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Thu, 9 Feb 2023 08:48:57 +0100 Subject: [PATCH 113/201] [fix] Don't trigger Finish event when in liveMode (#1115) --- packages/rrweb/src/replay/index.ts | 5 ++++- packages/rrweb/test/replayer.test.ts | 29 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index ce4dc6d8cb..dabfef88dd 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -709,7 +709,10 @@ export class Replayer { // events are kept sorted by timestamp, check if this is the last event const last_index = this.service.state.context.events.length - 1; - if (event === this.service.state.context.events[last_index]) { + if ( + !this.config.liveMode && + event === this.service.state.context.events[last_index] + ) { const finish = () => { if (last_index < this.service.state.context.events.length - 1) { // more events have been added since the setTimeout diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 6d94a47cd1..900f0eeb16 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -20,6 +20,7 @@ import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; import adoptedStyleSheet from './events/adopted-style-sheet'; import adoptedStyleSheetModification from './events/adopted-style-sheet-modification'; +import { ReplayerEvents } from '@rrweb/types'; interface ISuite { code: string; @@ -27,6 +28,9 @@ interface ISuite { page: puppeteer.Page; } +type IWindow = Window & + typeof globalThis & { rrweb: typeof import('../src'); events: typeof events }; + describe('replayer', function () { jest.setTimeout(10_000); @@ -45,7 +49,7 @@ describe('replayer', function () { page = await browser.newPage(); await page.goto('about:blank'); await page.evaluate(code); - await page.evaluate(`let events = ${JSON.stringify(events)}`); + await page.evaluate(`var events = ${JSON.stringify(events)}`); page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); }); @@ -679,6 +683,29 @@ describe('replayer', function () { expect(status).toEqual('live'); }); + it("shouldn't trigger ReplayerEvents.Finish in live mode", async () => { + const status = await page.evaluate((FinishState) => { + return new Promise((resolve) => { + const win = window as IWindow; + let triggeredFinish = false; + const { Replayer } = win.rrweb; + const replayer = new Replayer([], { + liveMode: true, + }); + replayer.on(FinishState, () => { + triggeredFinish = true; + }); + replayer.startLive(); + replayer.addEvent(win.events[0]); + requestAnimationFrame(() => { + resolve(triggeredFinish); + }); + }); + }, ReplayerEvents.Finish); + + expect(status).toEqual(false); + }); + it('replays same timestamp events in correct order', async () => { await page.evaluate(`events = ${JSON.stringify(orderingEvents)}`); await page.evaluate(` From cb1580008d04b0bc5c5d4ebec0e2e79899faaeb6 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 9 Feb 2023 09:19:53 +0100 Subject: [PATCH 114/201] fix: Ensure CSS support is checked more robustly (#1106) * fix: Ensure CSS support is checked more robustly * Apply formatting changes * apply eslint changes * Update packages/rrweb/src/record/observer.ts Co-authored-by: Justin Halsall * fix: do not use window in module scope --------- Co-authored-by: mydea Co-authored-by: Justin Halsall --- packages/rrweb/src/record/observer.ts | 42 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 324342e1db..e7284104b9 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -54,11 +54,6 @@ type WindowWithAngularZone = IWindow & { export const mutationBuffers: MutationBuffer[] = []; export const processedNodeManager = new ProcessedNodeManager(); -const isCSSGroupingRuleSupported = typeof CSSGroupingRule !== 'undefined'; -const isCSSMediaRuleSupported = typeof CSSMediaRule !== 'undefined'; -const isCSSSupportsRuleSupported = typeof CSSSupportsRule !== 'undefined'; -const isCSSConditionRuleSupported = typeof CSSConditionRule !== 'undefined'; - // Event.path is non-standard and used in some older browsers type NonStandardEvent = Omit & { path: EventTarget[]; @@ -488,13 +483,13 @@ function getNestedCSSRulePositions(rule: CSSRule): number[] { const positions: number[] = []; function recurse(childRule: CSSRule, pos: number[]) { if ( - (isCSSGroupingRuleSupported && + (hasNestedCSSRule('CSSGroupingRule') && childRule.parentRule instanceof CSSGroupingRule) || - (isCSSMediaRuleSupported && + (hasNestedCSSRule('CSSMediaRule') && childRule.parentRule instanceof CSSMediaRule) || - (isCSSSupportsRuleSupported && + (hasNestedCSSRule('CSSSupportsRule') && childRule.parentRule instanceof CSSSupportsRule) || - (isCSSConditionRuleSupported && + (hasNestedCSSRule('CSSConditionRule') && childRule.parentRule instanceof CSSConditionRule) ) { const rules = Array.from( @@ -643,20 +638,20 @@ function initStyleSheetObserver( const supportedNestedCSSRuleTypes: { [key: string]: GroupingCSSRuleTypes; } = {}; - if (isCSSGroupingRuleSupported) { + if (canMonkeyPatchNestedCSSRule('CSSGroupingRule')) { supportedNestedCSSRuleTypes.CSSGroupingRule = win.CSSGroupingRule; } else { // Some browsers (Safari) don't support CSSGroupingRule // https://caniuse.com/?search=cssgroupingrule // fall back to monkey patching classes that would have inherited from CSSGroupingRule - if (isCSSMediaRuleSupported) { + if (canMonkeyPatchNestedCSSRule('CSSMediaRule')) { supportedNestedCSSRuleTypes.CSSMediaRule = win.CSSMediaRule; } - if (isCSSConditionRuleSupported) { + if (canMonkeyPatchNestedCSSRule('CSSConditionRule')) { supportedNestedCSSRuleTypes.CSSConditionRule = win.CSSConditionRule; } - if (isCSSSupportsRuleSupported) { + if (canMonkeyPatchNestedCSSRule('CSSSupportsRule')) { supportedNestedCSSRuleTypes.CSSSupportsRule = win.CSSSupportsRule; } } @@ -1168,3 +1163,24 @@ export function initObservers( pluginHandlers.forEach((h) => h()); }; } + +type CSSGroupingProp = + | 'CSSGroupingRule' + | 'CSSMediaRule' + | 'CSSSupportsRule' + | 'CSSConditionRule'; + +function hasNestedCSSRule(prop: CSSGroupingProp): boolean { + return typeof window[prop] !== 'undefined'; +} + +function canMonkeyPatchNestedCSSRule(prop: CSSGroupingProp): boolean { + return Boolean( + typeof window[prop] !== 'undefined' && + // Note: Generally, this check _shouldn't_ be necessary + // However, in some scenarios (e.g. jsdom) this can sometimes fail, so we check for it here + window[prop].prototype && + 'insertRule' in window[prop].prototype && + 'deleteRule' in window[prop].prototype, + ); +} From 07326182f9750646771918481f116b946a17c2a9 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 19:23:19 +1100 Subject: [PATCH 115/201] fix: wrong rootId value in special iframes (#1100) 1. When some same-origin iframes are nested in cross-origin iframes, their `rootId`s are wrong. 2. The property `rootId` is missing in serialized cross-origin iframes --- packages/rrweb/src/record/iframe-manager.ts | 22 +- .../rrweb/test/benchmark/dom-mutation.test.ts | 3 +- .../benchmark/replay-fast-forward.test.ts | 3 +- .../cross-origin-iframes.test.ts.snap | 594 ++++++++++++++++++ .../test/record/cross-origin-iframes.test.ts | 23 +- 5 files changed, 639 insertions(+), 6 deletions(-) diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 58e830304b..d62713ff0d 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -1,5 +1,5 @@ import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import { genId } from 'rrweb-snapshot'; +import { genId, NodeType } from 'rrweb-snapshot'; import type { CrossOriginIframeMessageEvent } from '../types'; import CrossOriginIframeMirror from './cross-origin-iframe-mirror'; import { EventType, IncrementalSource } from '@rrweb/types'; @@ -14,6 +14,10 @@ export class IframeManager { > = new WeakMap(); public crossOriginIframeMirror = new CrossOriginIframeMirror(genId); public crossOriginIframeStyleMirror: CrossOriginIframeMirror; + public crossOriginIframeRootIdMap: WeakMap< + HTMLIFrameElement, + number + > = new WeakMap(); private mirror: Mirror; private mutationCb: mutationCallBack; private wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void; @@ -121,6 +125,9 @@ export class IframeManager { * Replaces the original id of the iframe with a new set of unique ids */ this.replaceIdOnNode(e.data.node, iframeEl); + const rootId = e.data.node.id; + this.crossOriginIframeRootIdMap.set(iframeEl, rootId); + this.patchRootIdOnNode(e.data.node, rootId); return { timestamp: e.timestamp, type: EventType.IncrementalSnapshot, @@ -171,6 +178,8 @@ export class IframeManager { 'previousId', ]); this.replaceIdOnNode(n.node, iframeEl); + const rootId = this.crossOriginIframeRootIdMap.get(iframeEl); + rootId && this.patchRootIdOnNode(n.node, rootId); }); e.data.removes.forEach((n) => { this.replaceIds(n, iframeEl, ['parentId', 'id']); @@ -273,11 +282,20 @@ export class IframeManager { node: serializedNodeWithId, iframeEl: HTMLIFrameElement, ) { - this.replaceIds(node, iframeEl, ['id']); + this.replaceIds(node, iframeEl, ['id', 'rootId']); if ('childNodes' in node) { node.childNodes.forEach((child) => { this.replaceIdOnNode(child, iframeEl); }); } } + + private patchRootIdOnNode(node: serializedNodeWithId, rootId: number) { + if (node.type !== NodeType.Document && !node.rootId) node.rootId = rootId; + if ('childNodes' in node) { + node.childNodes.forEach((child) => { + this.patchRootIdOnNode(child, rootId); + }); + } + } } diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts index 26f03280de..47fbfe5e39 100644 --- a/packages/rrweb/test/benchmark/dom-mutation.test.ts +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; import * as path from 'path'; import type { Page } from 'puppeteer'; -import type { eventWithTime, recordOptions } from '../../src/types'; +import type { eventWithTime } from '@rrweb/types'; +import type { recordOptions } from '../../src/types'; import { startServer, launchPuppeteer, ISuite, getServerURL } from '../utils'; const suites: Array< diff --git a/packages/rrweb/test/benchmark/replay-fast-forward.test.ts b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts index 4e17acdfdf..b5bb71a378 100644 --- a/packages/rrweb/test/benchmark/replay-fast-forward.test.ts +++ b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts @@ -1,6 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import type { eventWithTime, recordOptions } from '../../src/types'; +import type { eventWithTime } from '@rrweb/types'; +import type { recordOptions } from '../../src/types'; import { launchPuppeteer, ISuite } from '../utils'; const suites: Array<{ diff --git a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap index da4ff93d25..30178f6c64 100644 --- a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap +++ b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap @@ -106,6 +106,7 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -123,6 +124,7 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 15 }, { @@ -132,11 +134,13 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] \\"charset\\": \\"UTF-8\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 16 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 17 }, { @@ -147,11 +151,13 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] \\"content\\": \\"IE=edge\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 18 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 19 }, { @@ -162,11 +168,13 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] \\"content\\": \\"width=device-width, initial-scale=1.0\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 20 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 21 }, { @@ -177,14 +185,17 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"Audio\\", + \\"rootId\\": 11, \\"id\\": 23 } ], + \\"rootId\\": 11, \\"id\\": 22 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 24 }, { @@ -197,17 +208,21 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 26 } ], + \\"rootId\\": 11, \\"id\\": 25 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -218,6 +233,7 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 29 }, { @@ -228,14 +244,17 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"1 minute of silence\\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 30 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 32 }, { @@ -250,6 +269,7 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 34 }, { @@ -260,25 +280,31 @@ exports[`cross origin iframes audio.html should emit contents of iframe once 1`] \\"type\\": \\"audio/mpeg\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 35 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n Your browser does not support the audio element.\\\\n \\", + \\"rootId\\": 11, \\"id\\": 36 } ], + \\"rootId\\": 11, \\"id\\": 33 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 37 } ], + \\"rootId\\": 11, \\"id\\": 28 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -428,12 +454,15 @@ exports[`cross origin iframes blank.html should filter out forwarded cross origi { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 } ], + \\"rootId\\": 11, \\"id\\": 13 }, { @@ -444,12 +473,15 @@ exports[`cross origin iframes blank.html should filter out forwarded cross origi { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 12 } ], @@ -480,6 +512,7 @@ exports[`cross origin iframes blank.html should filter out forwarded cross origi \\"tagName\\": \\"iframe\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 18 } } @@ -517,12 +550,15 @@ exports[`cross origin iframes blank.html should filter out forwarded cross origi { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 19, \\"id\\": 23 } ], + \\"rootId\\": 19, \\"id\\": 22 } ], + \\"rootId\\": 19, \\"id\\": 21 }, { @@ -533,12 +569,259 @@ exports[`cross origin iframes blank.html should filter out forwarded cross origi { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n\\", + \\"rootId\\": 19, \\"id\\": 25 } ], + \\"rootId\\": 19, \\"id\\": 24 } ], + \\"rootId\\": 19, + \\"id\\": 20 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 19 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + } +]" +`; + +exports[`cross origin iframes blank.html should record same-origin iframe in cross-origin iframe 1`] = ` +"[ + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 6 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 8 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": { + \\"rr_src\\": \\"http://localhost:3030/html/blank.html\\" + }, + \\"childNodes\\": [], + \\"id\\": 9 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n \\", + \\"id\\": 10 + } + ], + \\"id\\": 7 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 9, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": { + \\"type\\": \\"\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, + \\"id\\": 15 + } + ], + \\"rootId\\": 11, + \\"id\\": 14 + } + ], + \\"rootId\\": 11, + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n\\", + \\"rootId\\": 11, + \\"id\\": 17 + } + ], + \\"rootId\\": 11, + \\"id\\": 16 + } + ], + \\"rootId\\": 11, + \\"id\\": 12 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 11 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"iframe\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 11, + \\"id\\": 18 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"adds\\": [ + { + \\"parentId\\": 18, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"rootId\\": 19, + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Same-origin iframe in cross-origin iframe\\", + \\"rootId\\": 19, + \\"id\\": 23 + } + ], + \\"rootId\\": 19, + \\"id\\": 22 + } + ], + \\"rootId\\": 19, \\"id\\": 20 } ], @@ -679,12 +962,15 @@ exports[`cross origin iframes blank.html should support packFn option in record( { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 } ], + \\"rootId\\": 11, \\"id\\": 13 }, { @@ -695,12 +981,15 @@ exports[`cross origin iframes blank.html should support packFn option in record( { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 12 } ], @@ -826,6 +1115,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -843,6 +1133,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 15 }, { @@ -852,11 +1143,13 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"charset\\": \\"UTF-8\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 16 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 17 }, { @@ -867,11 +1160,13 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"content\\": \\"width=device-width, initial-scale=1.0\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 18 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 19 }, { @@ -882,11 +1177,13 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"content\\": \\"ie=edge\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 20 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 21 }, { @@ -897,14 +1194,17 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"form fields\\", + \\"rootId\\": 11, \\"id\\": 23 } ], + \\"rootId\\": 11, \\"id\\": 22 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 24 }, { @@ -917,17 +1217,21 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 26 } ], + \\"rootId\\": 11, \\"id\\": 25 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -938,6 +1242,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 29 }, { @@ -948,6 +1253,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 }, { @@ -960,6 +1266,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 33 }, { @@ -969,19 +1276,23 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"type\\": \\"text\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 34 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 35 } ], + \\"rootId\\": 11, \\"id\\": 32 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 36 }, { @@ -992,6 +1303,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 38 }, { @@ -1003,19 +1315,23 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"value\\": \\"on\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 39 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 40 } ], + \\"rootId\\": 11, \\"id\\": 37 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 41 }, { @@ -1026,6 +1342,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 43 }, { @@ -1038,19 +1355,23 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"checked\\": true }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 44 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 45 } ], + \\"rootId\\": 11, \\"id\\": 42 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 46 }, { @@ -1063,6 +1384,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 48 }, { @@ -1072,19 +1394,23 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"type\\": \\"checkbox\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 49 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 50 } ], + \\"rootId\\": 11, \\"id\\": 47 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 51 }, { @@ -1097,6 +1423,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 53 }, { @@ -1109,19 +1436,23 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"rows\\": \\"10\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 54 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 55 } ], + \\"rootId\\": 11, \\"id\\": 52 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 56 }, { @@ -1134,6 +1465,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 58 }, { @@ -1148,6 +1480,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 60 }, { @@ -1161,14 +1494,17 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 62 } ], + \\"rootId\\": 11, \\"id\\": 61 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 63 }, { @@ -1181,30 +1517,37 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"2\\", + \\"rootId\\": 11, \\"id\\": 65 } ], + \\"rootId\\": 11, \\"id\\": 64 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 66 } ], + \\"rootId\\": 11, \\"id\\": 59 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 67 } ], + \\"rootId\\": 11, \\"id\\": 57 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 68 }, { @@ -1217,6 +1560,7 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 70 }, { @@ -1226,33 +1570,41 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"type\\": \\"password\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 71 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 72 } ], + \\"rootId\\": 11, \\"id\\": 69 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 73 } ], + \\"rootId\\": 11, \\"id\\": 30 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 74 } ], + \\"rootId\\": 11, \\"id\\": 28 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -1757,6 +2109,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -1774,6 +2127,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 15 }, { @@ -1783,11 +2137,13 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"charset\\": \\"UTF-8\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 16 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 17 }, { @@ -1798,11 +2154,13 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"content\\": \\"width=device-width, initial-scale=1.0\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 18 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 19 }, { @@ -1813,11 +2171,13 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"content\\": \\"ie=edge\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 20 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 21 }, { @@ -1828,14 +2188,17 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"form fields\\", + \\"rootId\\": 11, \\"id\\": 23 } ], + \\"rootId\\": 11, \\"id\\": 22 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 24 }, { @@ -1848,17 +2211,21 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 26 } ], + \\"rootId\\": 11, \\"id\\": 25 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -1869,6 +2236,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 29 }, { @@ -1879,6 +2247,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 }, { @@ -1891,6 +2260,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 33 }, { @@ -1900,19 +2270,23 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"type\\": \\"text\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 34 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 35 } ], + \\"rootId\\": 11, \\"id\\": 32 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 36 }, { @@ -1923,6 +2297,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 38 }, { @@ -1934,19 +2309,23 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"value\\": \\"on\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 39 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 40 } ], + \\"rootId\\": 11, \\"id\\": 37 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 41 }, { @@ -1957,6 +2336,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 43 }, { @@ -1969,19 +2349,23 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"checked\\": true }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 44 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 45 } ], + \\"rootId\\": 11, \\"id\\": 42 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 46 }, { @@ -1994,6 +2378,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 48 }, { @@ -2003,19 +2388,23 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"type\\": \\"checkbox\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 49 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 50 } ], + \\"rootId\\": 11, \\"id\\": 47 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 51 }, { @@ -2028,6 +2417,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 53 }, { @@ -2040,19 +2430,23 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"rows\\": \\"10\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 54 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 55 } ], + \\"rootId\\": 11, \\"id\\": 52 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 56 }, { @@ -2065,6 +2459,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 58 }, { @@ -2079,6 +2474,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 60 }, { @@ -2092,14 +2488,17 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 62 } ], + \\"rootId\\": 11, \\"id\\": 61 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 63 }, { @@ -2112,30 +2511,37 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"2\\", + \\"rootId\\": 11, \\"id\\": 65 } ], + \\"rootId\\": 11, \\"id\\": 64 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 66 } ], + \\"rootId\\": 11, \\"id\\": 59 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 67 } ], + \\"rootId\\": 11, \\"id\\": 57 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 68 }, { @@ -2148,6 +2554,7 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 70 }, { @@ -2157,33 +2564,41 @@ exports[`cross origin iframes form.html should map scroll events correctly 1`] = \\"type\\": \\"password\\" }, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 71 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 72 } ], + \\"rootId\\": 11, \\"id\\": 69 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 73 } ], + \\"rootId\\": 11, \\"id\\": 30 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 74 } ], + \\"rootId\\": 11, \\"id\\": 28 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -2335,6 +2750,7 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -2357,12 +2773,15 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -2373,6 +2792,7 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -2383,6 +2803,7 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -2390,19 +2811,23 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -2413,6 +2838,7 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -2423,6 +2849,7 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -2433,36 +2860,45 @@ exports[`cross origin iframes move-node.html captures mutations on adopted style { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -2728,6 +3164,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -2750,12 +3187,15 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -2766,6 +3206,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -2776,6 +3217,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -2783,19 +3225,23 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -2806,6 +3252,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -2816,6 +3263,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -2826,36 +3274,45 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -2907,6 +3364,7 @@ exports[`cross origin iframes move-node.html captures mutations on stylesheets 1 \\"tagName\\": \\"style\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 34 } } @@ -3125,6 +3583,7 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -3147,12 +3606,15 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -3163,6 +3625,7 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -3173,6 +3636,7 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -3180,19 +3644,23 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -3203,6 +3671,7 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -3213,6 +3682,7 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -3223,36 +3693,45 @@ exports[`cross origin iframes move-node.html should record DOM attribute changes { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -3392,6 +3871,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -3414,12 +3894,15 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -3430,6 +3913,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -3440,6 +3924,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -3447,19 +3932,23 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -3470,6 +3959,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -3480,6 +3970,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -3490,36 +3981,45 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -3552,6 +4052,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"node\\": { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 } }, @@ -3563,6 +4064,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"tagName\\": \\"i\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 26 } }, @@ -3572,6 +4074,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"node\\": { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 } }, @@ -3583,6 +4086,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"tagName\\": \\"b\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 28 } }, @@ -3592,6 +4096,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"node\\": { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } }, @@ -3601,6 +4106,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"node\\": { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } }, @@ -3610,6 +4116,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"node\\": { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } }, @@ -3621,6 +4128,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"tagName\\": \\"div\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 33 } }, @@ -3632,6 +4140,7 @@ exports[`cross origin iframes move-node.html should record DOM node movement 1`] \\"tagName\\": \\"span\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 24 } } @@ -3747,6 +4256,7 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -3769,12 +4279,15 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -3785,6 +4298,7 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -3795,6 +4309,7 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -3802,19 +4317,23 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -3825,6 +4344,7 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -3835,6 +4355,7 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -3845,36 +4366,45 @@ exports[`cross origin iframes move-node.html should record DOM node removal 1`] { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -4012,6 +4542,7 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -4034,12 +4565,15 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -4050,6 +4584,7 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -4060,6 +4595,7 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -4067,19 +4603,23 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -4090,6 +4630,7 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -4100,6 +4641,7 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -4110,36 +4652,45 @@ exports[`cross origin iframes move-node.html should record DOM text changes 1`] { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -4277,6 +4828,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -4299,12 +4851,15 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -4315,6 +4870,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -4325,6 +4881,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -4332,19 +4889,23 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -4355,6 +4916,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -4365,6 +4927,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -4375,36 +4938,45 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], @@ -4434,6 +5006,7 @@ exports[`cross origin iframes move-node.html should record canvas elements 1`] = \\"tagName\\": \\"canvas\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 33 } } @@ -4578,6 +5151,7 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` \\"name\\": \\"html\\", \\"publicId\\": \\"\\", \\"systemId\\": \\"\\", + \\"rootId\\": 11, \\"id\\": 12 }, { @@ -4600,12 +5174,15 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 11, \\"id\\": 16 } ], + \\"rootId\\": 11, \\"id\\": 15 } ], + \\"rootId\\": 11, \\"id\\": 14 }, { @@ -4616,6 +5193,7 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 18 }, { @@ -4626,6 +5204,7 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 20 }, { @@ -4633,19 +5212,23 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` \\"tagName\\": \\"p\\", \\"attributes\\": {}, \\"childNodes\\": [], + \\"rootId\\": 11, \\"id\\": 21 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 22 } ], + \\"rootId\\": 11, \\"id\\": 19 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 23 }, { @@ -4656,6 +5239,7 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 25 }, { @@ -4666,6 +5250,7 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 27 }, { @@ -4676,36 +5261,45 @@ exports[`cross origin iframes move-node.html should record custom events 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"1\\", + \\"rootId\\": 11, \\"id\\": 29 } ], + \\"rootId\\": 11, \\"id\\": 28 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 30 } ], + \\"rootId\\": 11, \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 11, \\"id\\": 31 } ], + \\"rootId\\": 11, \\"id\\": 24 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 11, \\"id\\": 32 } ], + \\"rootId\\": 11, \\"id\\": 17 } ], + \\"rootId\\": 11, \\"id\\": 13 } ], diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index 0ad704e92b..3fa9446270 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -517,6 +517,24 @@ describe('cross origin iframes', function (this: ISuite) { serverBURL: string; }; + it('should record same-origin iframe in cross-origin iframe', async () => { + const frame = ctx.page.mainFrame().childFrames()[0]; + await frame.evaluate(() => { + const iframe2 = document.createElement('iframe'); + // Append a same-origin iframe in a cross-origin iframe. + document.body.appendChild(iframe2); + iframe2.contentDocument!.body.appendChild( + document.createTextNode('Same-origin iframe in cross-origin iframe'), + ); + }); + + await waitForRAF(ctx.page); + const snapshots = (await ctx.page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + it('should filter out forwarded cross origin rrweb messages', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; const iframe2URL = `${ctx.serverBURL}/html/blank.html`; @@ -533,10 +551,11 @@ describe('cross origin iframes', function (this: ISuite) { // Wait for iframe2 to load await ctx.page.waitForFrame(iframe2URL); + const iframe2 = frame.childFrames()[0]; // Record iframe2 - await injectRecordScript(frame.childFrames()[0]); + await injectRecordScript(iframe2); - await waitForRAF(frame.childFrames()[0]); + await waitForRAF(iframe2); const snapshots = (await ctx.page.evaluate( 'window.snapshots', )) as eventWithTime[]; From 3caa25ed9b19954c98775f22d5fa47233fa3d1db Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Thu, 9 Feb 2023 09:02:38 +0000 Subject: [PATCH 116/201] Don't have requestAnimationFrame looping in background for Live Mode (#1098) --- packages/rrweb/src/replay/index.ts | 11 ++--- packages/rrweb/src/replay/machine.ts | 1 - packages/rrweb/src/replay/timer.ts | 65 +++++++++++++++------------- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index dabfef88dd..0e2abb119f 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -300,7 +300,6 @@ export class Replayer { const timer = new Timer([], { speed: this.config.speed, - liveMode: this.config.liveMode, }); this.service = createPlayerService( { @@ -722,18 +721,16 @@ export class Replayer { this.service.send('END'); this.emitter.emit(ReplayerEvents.Finish); }; + let finish_buffer = 50; // allow for checking whether new events aren't just about to be loaded in if ( event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.MouseMove && event.data.positions.length ) { - // defer finish event if the last event is a mouse move - setTimeout(() => { - finish(); - }, Math.max(0, -event.data.positions[0].timeOffset + 50)); // Add 50 to make sure the timer would check the last mousemove event. Otherwise, the timer may be stopped by the service before checking the last event. - } else { - finish(); + // extend finish event if the last event is a mouse move so that the timer isn't stopped by the service before checking the last event + finish_buffer += Math.max(0, -event.data.positions[0].timeOffset); } + setTimeout(finish, finish_buffer); } this.emitter.emit(ReplayerEvents.EventCast, event); diff --git a/packages/rrweb/src/replay/machine.ts b/packages/rrweb/src/replay/machine.ts index 9d09123f12..07d948fdca 100644 --- a/packages/rrweb/src/replay/machine.ts +++ b/packages/rrweb/src/replay/machine.ts @@ -223,7 +223,6 @@ export function createPlayerService( }), startLive: assign({ baselineTime: (ctx, event) => { - ctx.timer.toggleLiveMode(true); ctx.timer.start(); if (event.type === 'TO_LIVE' && event.payload.baselineTime) { return event.payload.baselineTime; diff --git a/packages/rrweb/src/replay/timer.ts b/packages/rrweb/src/replay/timer.ts index e25e6a4647..0d18c3f870 100644 --- a/packages/rrweb/src/replay/timer.ts +++ b/packages/rrweb/src/replay/timer.ts @@ -10,64 +10,71 @@ export class Timer { public speed: number; private actions: actionWithDelay[]; - private raf: number | null = null; - private liveMode: boolean; + private raf: number | true | null = null; + private lastTimestamp: number; constructor( actions: actionWithDelay[] = [], config: { speed: number; - liveMode: boolean; }, ) { this.actions = actions; this.speed = config.speed; - this.liveMode = config.liveMode; } /** * Add an action, possibly after the timer starts. */ public addAction(action: actionWithDelay) { + const rafWasActive = this.raf === true; if ( !this.actions.length || this.actions[this.actions.length - 1].delay <= action.delay ) { // 'fast track' this.actions.push(action); - return; + } else { + // binary search - events can arrive out of order in a realtime context + const index = this.findActionIndex(action); + this.actions.splice(index, 0, action); + } + if (rafWasActive) { + this.raf = requestAnimationFrame(this.rafCheck.bind(this)); } - // binary search - events can arrive out of order in a realtime context - const index = this.findActionIndex(action); - this.actions.splice(index, 0, action); } public start() { this.timeOffset = 0; - let lastTimestamp = performance.now(); - const check = () => { - const time = performance.now(); - this.timeOffset += (time - lastTimestamp) * this.speed; - lastTimestamp = time; - while (this.actions.length) { - const action = this.actions[0]; + this.lastTimestamp = performance.now(); + this.raf = requestAnimationFrame(this.rafCheck.bind(this)); + } - if (this.timeOffset >= action.delay) { - this.actions.shift(); - action.doAction(); - } else { - break; - } - } - if (this.actions.length > 0 || this.liveMode) { - this.raf = requestAnimationFrame(check); + private rafCheck() { + const time = performance.now(); + this.timeOffset += (time - this.lastTimestamp) * this.speed; + this.lastTimestamp = time; + while (this.actions.length) { + const action = this.actions[0]; + + if (this.timeOffset >= action.delay) { + this.actions.shift(); + action.doAction(); + } else { + break; } - }; - this.raf = requestAnimationFrame(check); + } + if (this.actions.length > 0) { + this.raf = requestAnimationFrame(this.rafCheck.bind(this)); + } else { + this.raf = true; // was active + } } public clear() { if (this.raf) { - cancelAnimationFrame(this.raf); + if (this.raf !== true) { + cancelAnimationFrame(this.raf); + } this.raf = null; } this.actions.length = 0; @@ -77,10 +84,6 @@ export class Timer { this.speed = speed; } - public toggleLiveMode(mode: boolean) { - this.liveMode = mode; - } - public isActive() { return this.raf !== null; } From 3a26e36f6f625c0391c7e6d3f1050660adfccc4f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 20:09:58 +1100 Subject: [PATCH 117/201] fix: regression of issue: ShadowHost can't be a string (issue 941) (#1092) --- packages/rrweb/src/record/mutation.ts | 10 +--- packages/rrweb/src/utils.ts | 33 +++++++++---- packages/rrweb/test/util.test.ts | 70 ++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index bcc316ea1d..d9e27496a2 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -27,6 +27,7 @@ import { isSerializedIframe, isSerializedStylesheet, inDom, + getShadowHost, } from '../utils'; type DoubleLinkedListNode = { @@ -268,18 +269,11 @@ export default class MutationBuffer { return nextId; }; const pushAdd = (n: Node) => { - let shadowHost: Element | null = null; - if ( - n.getRootNode?.()?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && - (n.getRootNode() as ShadowRoot).host - ) - shadowHost = (n.getRootNode() as ShadowRoot).host; - if (!n.parentNode || !inDom(n)) { return; } const parentId = isShadowRoot(n.parentNode) - ? this.mirror.getId(shadowHost) + ? this.mirror.getId(getShadowHost(n)) : this.mirror.getId(n.parentNode); const nextId = getNextId(n); if (parentId === -1 || nextId === -1) { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 1d5ec83855..8703191f6d 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -519,16 +519,29 @@ export class StyleSheetMirror { } } -export function getRootShadowHost(n: Node): Node | null { - const shadowHost = (n.getRootNode() as ShadowRoot).host; - // If n is in a nested shadow dom. - let rootShadowHost = shadowHost; - - while ( - rootShadowHost?.getRootNode?.()?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && - (rootShadowHost.getRootNode() as ShadowRoot).host +/** + * Get the direct shadow host of a node in shadow dom. Returns null if it is not in a shadow dom. + */ +export function getShadowHost(n: Node): Element | null { + let shadowHost: Element | null = null; + if ( + n.getRootNode?.()?.nodeType === Node.DOCUMENT_FRAGMENT_NODE && + (n.getRootNode() as ShadowRoot).host ) - rootShadowHost = (rootShadowHost.getRootNode() as ShadowRoot).host; + shadowHost = (n.getRootNode() as ShadowRoot).host; + return shadowHost; +} + +/** + * Get the root shadow host of a node in nested shadow doms. Returns the node itself if it is not in a shadow dom. + */ +export function getRootShadowHost(n: Node): Node { + let rootShadowHost: Node = n; + + let shadowHost: Element | null; + // If n is in a nested shadow dom. + while ((shadowHost = getShadowHost(rootShadowHost))) + rootShadowHost = shadowHost; return rootShadowHost; } @@ -537,7 +550,7 @@ export function shadowHostInDom(n: Node): boolean { const doc = n.ownerDocument; if (!doc) return false; const shadowHost = getRootShadowHost(n); - return Boolean(shadowHost && doc.contains(shadowHost)); + return doc.contains(shadowHost); } export function inDom(n: Node): boolean { diff --git a/packages/rrweb/test/util.test.ts b/packages/rrweb/test/util.test.ts index 964005c8c6..46ae81db2a 100644 --- a/packages/rrweb/test/util.test.ts +++ b/packages/rrweb/test/util.test.ts @@ -1,7 +1,13 @@ /** * @jest-environment jsdom */ -import { StyleSheetMirror } from '../src/utils'; +import { + getRootShadowHost, + StyleSheetMirror, + inDom, + shadowHostInDom, + getShadowHost, +} from '../src/utils'; describe('Utilities for other modules', () => { describe('StyleSheetMirror', () => { @@ -75,4 +81,66 @@ describe('Utilities for other modules', () => { expect(mirror.add(new CSSStyleSheet())).toBe(1); }); }); + + describe('inDom()', () => { + it('should get correct result given nested shadow doms', () => { + const shadowHost = document.createElement('div'); + const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); + const shadowHost2 = document.createElement('div'); + const shadowRoot2 = shadowHost2.attachShadow({ mode: 'open' }); + const div = document.createElement('div'); + shadowRoot.appendChild(shadowHost2); + shadowRoot2.appendChild(div); + // Not in Dom yet. + expect(getShadowHost(div)).toBe(shadowHost2); + expect(getRootShadowHost(div)).toBe(shadowHost); + expect(shadowHostInDom(div)).toBeFalsy(); + expect(inDom(div)).toBeFalsy(); + + // Added to the Dom. + document.body.appendChild(shadowHost); + expect(getShadowHost(div)).toBe(shadowHost2); + expect(getRootShadowHost(div)).toBe(shadowHost); + expect(shadowHostInDom(div)).toBeTruthy(); + expect(inDom(div)).toBeTruthy(); + }); + + it('should get correct result given a normal node', () => { + const div = document.createElement('div'); + // Not in Dom yet. + expect(getShadowHost(div)).toBeNull(); + expect(getRootShadowHost(div)).toBe(div); + expect(shadowHostInDom(div)).toBeFalsy(); + expect(inDom(div)).toBeFalsy(); + + // Added to the Dom. + document.body.appendChild(div); + expect(getShadowHost(div)).toBeNull(); + expect(getRootShadowHost(div)).toBe(div); + expect(shadowHostInDom(div)).toBeTruthy(); + expect(inDom(div)).toBeTruthy(); + }); + + /** + * Given the textNode of a detached HTMLAnchorElement, getRootNode() will return the anchor element itself and its host property is a string. + * This corner case may cause an error in getRootShadowHost(). + */ + it('should get correct result given the textNode of a detached HTMLAnchorElement', () => { + const a = document.createElement('a'); + a.href = 'example.com'; + a.textContent = 'something'; + // Not in Dom yet. + expect(getShadowHost(a.childNodes[0])).toBeNull(); + expect(getRootShadowHost(a.childNodes[0])).toBe(a.childNodes[0]); + expect(shadowHostInDom(a.childNodes[0])).toBeFalsy(); + expect(inDom(a.childNodes[0])).toBeFalsy(); + + // Added to the Dom. + document.body.appendChild(a); + expect(getShadowHost(a.childNodes[0])).toBeNull(); + expect(getRootShadowHost(a.childNodes[0])).toBe(a.childNodes[0]); + expect(shadowHostInDom(a.childNodes[0])).toBeTruthy(); + expect(inDom(a.childNodes[0])).toBeTruthy(); + }); + }); }); From 4ee86fe66d3e1fe7071f9c8764d82a6fa5c71d57 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 20:10:32 +1100 Subject: [PATCH 118/201] improve rrdom robustness (#1091) --- packages/rrdom/src/diff.ts | 408 ++++++++++++------ packages/rrdom/src/document.ts | 10 + packages/rrdom/test/diff.test.ts | 548 +++++++++++++++++++++++- packages/rrdom/test/document.test.ts | 2 +- packages/rrdom/test/utils.ts | 26 ++ packages/rrdom/test/virtual-dom.test.ts | 24 +- packages/rrweb/src/replay/index.ts | 28 +- 7 files changed, 871 insertions(+), 175 deletions(-) create mode 100644 packages/rrdom/test/utils.ts diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index f03557e7f5..842bfb8953 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -1,4 +1,8 @@ -import { NodeType as RRNodeType, Mirror as NodeMirror } from 'rrweb-snapshot'; +import { + NodeType as RRNodeType, + Mirror as NodeMirror, + elementNode, +} from 'rrweb-snapshot'; import type { canvasMutationData, canvasEventWithTime, @@ -10,7 +14,6 @@ import type { import type { IRRCDATASection, IRRComment, - IRRDocument, IRRDocumentType, IRRElement, IRRNode, @@ -85,21 +88,36 @@ export type ReplayerHandler = { data: styleDeclarationData | styleSheetRuleData, styleSheet: CSSStyleSheet, ) => void; + // Similar to the `afterAppend` callback in the `rrweb-snapshot` package. It's a postorder traversal of the newly appended nodes. + afterAppend?(node: Node, id: number): void; }; +// A set contains newly appended nodes. It's used to make sure the afterAppend callback can iterate newly appended nodes in the same traversal order as that in the `rrweb-snapshot` package. +let createdNodeSet: WeakSet | null = null; + +/** + * Make the old tree to have the same structure and properties as the new tree with the diff algorithm. + * @param oldTree - The old tree to be modified. + * @param newTree - The new tree which the old tree will be modified to. + * @param replayer - A slimmed replayer instance including the mirror of the old tree. + * @param rrnodeMirror - The mirror of the new tree. + */ export function diff( oldTree: Node, newTree: IRRNode, replayer: ReplayerHandler, - rrnodeMirror?: Mirror, + rrnodeMirror: Mirror = (newTree as RRDocument).mirror || + (newTree.ownerDocument as RRDocument).mirror, ) { + oldTree = diffBeforeUpdatingChildren( + oldTree, + newTree, + replayer, + rrnodeMirror, + ); + const oldChildren = oldTree.childNodes; const newChildren = newTree.childNodes; - rrnodeMirror = - rrnodeMirror || - (newTree as RRDocument).mirror || - (newTree.ownerDocument as RRDocument).mirror; - if (oldChildren.length > 0 || newChildren.length > 0) { diffChildren( Array.from(oldChildren), @@ -110,20 +128,119 @@ export function diff( ); } - let inputDataToApply = null, - scrollDataToApply = null; + diffAfterUpdatingChildren(oldTree, newTree, replayer, rrnodeMirror); +} + +/** + * Do some preparation work before updating the children of the old tree. + */ +function diffBeforeUpdatingChildren( + oldTree: Node, + newTree: IRRNode, + replayer: ReplayerHandler, + rrnodeMirror: Mirror, +) { + if (replayer.afterAppend && !createdNodeSet) { + createdNodeSet = new WeakSet(); + setTimeout(() => { + createdNodeSet = null; + }, 0); + } + // If the Mirror data has some flaws, the diff function may throw errors. We check the node consistency here to make it robust. + if (!sameNodeType(oldTree, newTree)) { + const calibratedOldTree = createOrGetNode( + newTree, + replayer.mirror, + rrnodeMirror, + ); + oldTree.parentNode?.replaceChild(calibratedOldTree, oldTree); + oldTree = calibratedOldTree; + } switch (newTree.RRNodeType) { case RRNodeType.Document: { - const newRRDocument = newTree as IRRDocument; - scrollDataToApply = (newRRDocument as RRDocument).scrollData; + /** + * Special cases for updating the document node: + * Case 1: If the oldTree is the content document of an iframe element and its content (HTML, HEAD, and BODY) is automatically mounted by browsers, we need to remove them to avoid unexpected behaviors. e.g. Selector matches may be case insensitive. + * Case 2: The newTree has a different serialized Id (a different document object), we need to reopen it and update the nodeMirror. + */ + if (!nodeMatching(oldTree, newTree, replayer.mirror, rrnodeMirror)) { + const newMeta = rrnodeMirror.getMeta(newTree); + if (newMeta) { + replayer.mirror.removeNodeFromMap(oldTree); + (oldTree as Document).close(); + (oldTree as Document).open(); + replayer.mirror.add(oldTree, newMeta); + createdNodeSet?.add(oldTree); + } + } break; } case RRNodeType.Element: { const oldElement = oldTree as HTMLElement; const newRRElement = newTree as IRRElement; + switch (newRRElement.tagName) { + case 'IFRAME': { + const oldContentDocument = (oldTree as HTMLIFrameElement) + .contentDocument; + // If the iframe is cross-origin, the contentDocument will be null. + if (!oldContentDocument) break; + // IFrame element doesn't have child nodes, so here we update its content document separately. + diff( + oldContentDocument, + (newTree as RRIFrameElement).contentDocument, + replayer, + rrnodeMirror, + ); + break; + } + } + if (newRRElement.shadowRoot) { + if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: 'open' }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const oldChildren = oldElement.shadowRoot!.childNodes; + const newChildren = newRRElement.shadowRoot.childNodes; + if (oldChildren.length > 0 || newChildren.length > 0) + diffChildren( + Array.from(oldChildren), + newChildren, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + oldElement.shadowRoot!, + replayer, + rrnodeMirror, + ); + } + break; + } + } + return oldTree; +} + +/** + * Finish the diff work after updating the children of the old tree. + */ +function diffAfterUpdatingChildren( + oldTree: Node, + newTree: IRRNode, + replayer: ReplayerHandler, + rrnodeMirror: Mirror, +) { + switch (newTree.RRNodeType) { + case RRNodeType.Document: { + const scrollData = (newTree as RRDocument).scrollData; + scrollData && replayer.applyScroll(scrollData, true); + break; + } + case RRNodeType.Element: { + const oldElement = oldTree as HTMLElement; + const newRRElement = newTree as RRElement; diffProps(oldElement, newRRElement, rrnodeMirror); - scrollDataToApply = (newRRElement as RRElement).scrollData; - inputDataToApply = (newRRElement as RRElement).inputData; + newRRElement.scrollData && + replayer.applyScroll(newRRElement.scrollData, true); + /** + * Input data need to get applied after all children of this node are updated. + * Otherwise when we set a value for a select element whose options are empty, the value won't actually update. + */ + newRRElement.inputData && replayer.applyInput(newRRElement.inputData); switch (newRRElement.tagName) { case 'AUDIO': case 'VIDEO': { @@ -143,59 +260,43 @@ export function diff( oldMediaElement.playbackRate = newMediaRRElement.playbackRate; break; } - case 'CANVAS': - { - const rrCanvasElement = newTree as RRCanvasElement; - // This canvas element is created with initial data in an iframe element. https://github.com/rrweb-io/rrweb/pull/944 - if (rrCanvasElement.rr_dataURL !== null) { - const image = document.createElement('img'); - image.onload = () => { - const ctx = (oldElement as HTMLCanvasElement).getContext('2d'); - if (ctx) { - ctx.drawImage(image, 0, 0, image.width, image.height); - } - }; - image.src = rrCanvasElement.rr_dataURL; - } - rrCanvasElement.canvasMutations.forEach((canvasMutation) => - replayer.applyCanvas( - canvasMutation.event, - canvasMutation.mutation, - oldTree as HTMLCanvasElement, - ), - ); + case 'CANVAS': { + const rrCanvasElement = newTree as RRCanvasElement; + // This canvas element is created with initial data in an iframe element. https://github.com/rrweb-io/rrweb/pull/944 + if (rrCanvasElement.rr_dataURL !== null) { + const image = document.createElement('img'); + image.onload = () => { + const ctx = (oldElement as HTMLCanvasElement).getContext('2d'); + if (ctx) { + ctx.drawImage(image, 0, 0, image.width, image.height); + } + }; + image.src = rrCanvasElement.rr_dataURL; } + rrCanvasElement.canvasMutations.forEach((canvasMutation) => + replayer.applyCanvas( + canvasMutation.event, + canvasMutation.mutation, + oldTree as HTMLCanvasElement, + ), + ); break; - case 'STYLE': - { - const styleSheet = (oldElement as HTMLStyleElement).sheet; - styleSheet && - (newTree as RRStyleElement).rules.forEach((data) => - replayer.applyStyleSheetMutation(data, styleSheet), - ); - } + } + // Props of style elements have to be updated after all children are updated. Otherwise the props can be overwritten by textContent. + case 'STYLE': { + const styleSheet = (oldElement as HTMLStyleElement).sheet; + styleSheet && + (newTree as RRStyleElement).rules.forEach((data) => + replayer.applyStyleSheetMutation(data, styleSheet), + ); break; - } - if (newRRElement.shadowRoot) { - if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: 'open' }); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const oldChildren = oldElement.shadowRoot!.childNodes; - const newChildren = newRRElement.shadowRoot.childNodes; - if (oldChildren.length > 0 || newChildren.length > 0) - diffChildren( - Array.from(oldChildren), - newChildren, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - oldElement.shadowRoot!, - replayer, - rrnodeMirror, - ); + } } break; } case RRNodeType.Text: case RRNodeType.Comment: - case RRNodeType.CDATA: + case RRNodeType.CDATA: { if ( oldTree.textContent !== (newTree as IRRText | IRRComment | IRRCDATASection).data @@ -205,34 +306,12 @@ export function diff( | IRRComment | IRRCDATASection).data; break; - default: - } - - scrollDataToApply && replayer.applyScroll(scrollDataToApply, true); - /** - * Input data need to get applied after all children of this node are updated. - * Otherwise when we set a value for a select element whose options are empty, the value won't actually update. - */ - inputDataToApply && replayer.applyInput(inputDataToApply); - - // IFrame element doesn't have child nodes. - if (newTree.nodeName === 'IFRAME') { - const oldContentDocument = (oldTree as HTMLIFrameElement).contentDocument; - const newIFrameElement = newTree as RRIFrameElement; - // If the iframe is cross-origin, the contentDocument will be null. - if (oldContentDocument) { - const sn = rrnodeMirror.getMeta(newIFrameElement.contentDocument); - if (sn) { - replayer.mirror.add(oldContentDocument, { ...sn }); - } - diff( - oldContentDocument, - newIFrameElement.contentDocument, - replayer, - rrnodeMirror, - ); } } + if (createdNodeSet?.has(oldTree)) { + createdNodeSet.delete(oldTree); + replayer.afterAppend?.(oldTree, replayer.mirror.getId(oldTree)); + } } function diffProps( @@ -245,8 +324,8 @@ function diffProps( for (const name in newAttributes) { const newValue = newAttributes[name]; - const sn = rrnodeMirror.getMeta(newTree); - if (sn && 'isSVG' in sn && sn.isSVG && NAMESPACES[name]) + const sn = rrnodeMirror.getMeta(newTree) as elementNode | null; + if (sn?.isSVG && NAMESPACES[name]) oldTree.setAttributeNS(NAMESPACES[name], name, newValue); else if (newTree.tagName === 'CANVAS' && name === 'rr_dataURL') { const image = document.createElement('img'); @@ -283,53 +362,47 @@ function diffChildren( newStartNode = newChildren[newStartIndex], newEndNode = newChildren[newEndIndex]; let oldIdToIndex: Record | undefined = undefined, - indexInOld; + indexInOld: number | undefined = undefined; while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) { - const oldStartId = replayer.mirror.getId(oldStartNode); - const oldEndId = replayer.mirror.getId(oldEndNode); - const newStartId = rrnodeMirror.getId(newStartNode); - const newEndId = rrnodeMirror.getId(newEndNode); - - // rrdom contains elements with negative ids, we don't want to accidentally match those to a mirror mismatch (-1) id. - // Negative oldStartId happen when nodes are not in the mirror, but are in the DOM. - // eg.iframes come with a document, html, head and body nodes. - // thats why below we always check if an id is negative. - if (oldStartNode === undefined) { oldStartNode = oldChildren[++oldStartIndex]; } else if (oldEndNode === undefined) { oldEndNode = oldChildren[--oldEndIndex]; } else if ( - oldStartId !== -1 && - // same first element? - oldStartId === newStartId + // same first node? + nodeMatching(oldStartNode, newStartNode, replayer.mirror, rrnodeMirror) ) { diff(oldStartNode, newStartNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newStartNode = newChildren[++newStartIndex]; } else if ( - oldEndId !== -1 && - // same last element? - oldEndId === newEndId + // same last node? + nodeMatching(oldEndNode, newEndNode, replayer.mirror, rrnodeMirror) ) { diff(oldEndNode, newEndNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( - oldStartId !== -1 && - // is the first old element the same as the last new element? - oldStartId === newEndId + // is the first old node the same as the last new node? + nodeMatching(oldStartNode, newEndNode, replayer.mirror, rrnodeMirror) ) { - parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling); + try { + parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling); + } catch (e) { + console.warn(e); + } diff(oldStartNode, newEndNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( - oldEndId !== -1 && - // is the last old element the same as the first new element? - oldEndId === newStartId + // is the last old node the same as the first new node? + nodeMatching(oldEndNode, newStartNode, replayer.mirror, rrnodeMirror) ) { - parentNode.insertBefore(oldEndNode, oldStartNode); + try { + parentNode.insertBefore(oldEndNode, oldStartNode); + } catch (e) { + console.warn(e); + } diff(oldEndNode, newStartNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newStartNode = newChildren[++newStartIndex]; @@ -345,10 +418,17 @@ function diffChildren( } } indexInOld = oldIdToIndex[rrnodeMirror.getId(newStartNode)]; - if (indexInOld) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const nodeToMove = oldChildren[indexInOld]!; - parentNode.insertBefore(nodeToMove, oldStartNode); + const nodeToMove = oldChildren[indexInOld]; + if ( + indexInOld !== undefined && + nodeToMove && + nodeMatching(nodeToMove, newStartNode, replayer.mirror, rrnodeMirror) + ) { + try { + parentNode.insertBefore(nodeToMove, oldStartNode); + } catch (e) { + console.warn(e); + } diff(nodeToMove, newStartNode, replayer, rrnodeMirror); oldChildren[indexInOld] = undefined; } else { @@ -358,50 +438,66 @@ function diffChildren( rrnodeMirror, ); - /** - * A mounted iframe element has an automatically created HTML element. - * We should delete it before insert a serialized one. Otherwise, an error 'Only one element on document allowed' will be thrown. - */ if ( parentNode.nodeName === '#document' && - replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element && - (parentNode as Document).documentElement + oldStartNode && + /** + * Special case 1: one document isn't allowed to have two doctype nodes at the same time, so we need to remove the old one first before inserting the new one. + * How this case happens: A parent document in the old tree already has a doctype node with an id e.g. #1. A new full snapshot rebuilds the replayer with a new doctype node with another id #2. According to the algorithm, the new doctype node will be inserted before the old one, which is not allowed by the Document standard. + */ + ((newNode.nodeType === newNode.DOCUMENT_TYPE_NODE && + oldStartNode.nodeType === oldStartNode.DOCUMENT_TYPE_NODE) || + /** + * Special case 2: one document isn't allowed to have two HTMLElements at the same time, so we need to remove the old one first before inserting the new one. + * How this case happens: A mounted iframe element has an automatically created HTML element. We should delete it before inserting a serialized one. Otherwise, an error 'Only one element on document allowed' will be thrown. + */ + (newNode.nodeType === newNode.ELEMENT_NODE && + oldStartNode.nodeType === oldStartNode.ELEMENT_NODE)) ) { - parentNode.removeChild((parentNode as Document).documentElement); - oldChildren[oldStartIndex] = undefined; - oldStartNode = undefined; + parentNode.removeChild(oldStartNode); + replayer.mirror.removeNodeFromMap(oldStartNode); + oldStartNode = oldChildren[++oldStartIndex]; + } + + try { + parentNode.insertBefore(newNode, oldStartNode || null); + diff(newNode, newStartNode, replayer, rrnodeMirror); + } catch (e) { + console.warn(e); } - parentNode.insertBefore(newNode, oldStartNode || null); - diff(newNode, newStartNode, replayer, rrnodeMirror); } newStartNode = newChildren[++newStartIndex]; } } if (oldStartIndex > oldEndIndex) { const referenceRRNode = newChildren[newEndIndex + 1]; - let referenceNode = null; + let referenceNode: Node | null = null; if (referenceRRNode) - parentNode.childNodes.forEach((child) => { - if ( - replayer.mirror.getId(child) === rrnodeMirror.getId(referenceRRNode) - ) - referenceNode = child; - }); + referenceNode = replayer.mirror.getNode( + rrnodeMirror.getId(referenceRRNode), + ); for (; newStartIndex <= newEndIndex; ++newStartIndex) { const newNode = createOrGetNode( newChildren[newStartIndex], replayer.mirror, rrnodeMirror, ); - parentNode.insertBefore(newNode, referenceNode); - diff(newNode, newChildren[newStartIndex], replayer, rrnodeMirror); + try { + parentNode.insertBefore(newNode, referenceNode); + diff(newNode, newChildren[newStartIndex], replayer, rrnodeMirror); + } catch (e) { + console.warn(e); + } } } else if (newStartIndex > newEndIndex) { for (; oldStartIndex <= oldEndIndex; oldStartIndex++) { const node = oldChildren[oldStartIndex]; - if (node) { + if (!node || !parentNode.contains(node)) continue; + try { parentNode.removeChild(node); replayer.mirror.removeNodeFromMap(node); + } catch (e) { + console.warn(e); } } } @@ -417,7 +513,7 @@ export function createOrGetNode( let node: Node | null = null; // negative ids shouldn't be compared accross mirrors if (nodeId > -1) node = domMirror.getNode(nodeId); - if (node !== null) return node; + if (node !== null && sameNodeType(node, rrNode)) return node; switch (rrNode.RRNodeType) { case RRNodeType.Document: node = new Document(); @@ -449,5 +545,41 @@ export function createOrGetNode( } if (sn) domMirror.add(node, { ...sn }); + try { + createdNodeSet?.add(node); + } catch (e) { + // Just for safety concern. + } return node; } + +/** + * To check whether two nodes are the same type of node. If they are both Elements, check wether their tagNames are same or not. + */ +export function sameNodeType(node1: Node, node2: IRRNode) { + if (node1.nodeType !== node2.nodeType) return false; + return ( + node1.nodeType !== node1.ELEMENT_NODE || + (node1 as HTMLElement).tagName.toUpperCase() === + (node2 as IRRElement).tagName + ); +} + +/** + * To check whether two nodes are matching. If so, they are supposed to have the same serialized Id and node type. If they are both Elements, their tagNames should be the same as well. Otherwise, they are not matching. + */ +export function nodeMatching( + node1: Node, + node2: IRRNode, + domMirror: NodeMirror, + rrdomMirror: Mirror, +): boolean { + const node1Id = domMirror.getId(node1); + const node2Id = rrdomMirror.getId(node2); + // rrdom contains elements with negative ids, we don't want to accidentally match those to a mirror mismatch (-1) id. + // Negative oldStartId happen when nodes are not in the mirror, but are in the DOM. + // eg.iframes come with a document, html, head and body nodes. + // thats why below we always check if an id is negative. + if (node1Id === -1 || node1Id !== node2Id) return false; + return sameNodeType(node1, node2); +} diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index f8ee5b8686..9f76f82123 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -204,6 +204,12 @@ export function BaseRRDocumentImpl< public readonly RRNodeType = RRNodeType.Document; public textContent: string | null = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(...args: any[]) { + super(args); + this.ownerDocument = this; + } + public get documentElement(): IRRElement | null { return ( (this.childNodes.find( @@ -258,6 +264,7 @@ export function BaseRRDocumentImpl< } childNode.parentElement = null; childNode.parentNode = this; + childNode.ownerDocument = this.ownerDocument; this.childNodes.push(childNode); return childNode; } @@ -285,6 +292,7 @@ export function BaseRRDocumentImpl< this.childNodes.splice(childIndex, 0, newChild); newChild.parentElement = null; newChild.parentNode = this; + newChild.ownerDocument = this.ownerDocument; return newChild; } @@ -522,6 +530,7 @@ export function BaseRRElementImpl< this.childNodes.push(newChild); newChild.parentNode = this; newChild.parentElement = this; + newChild.ownerDocument = this.ownerDocument; return newChild; } @@ -535,6 +544,7 @@ export function BaseRRElementImpl< this.childNodes.splice(childIndex, 0, newChild); newChild.parentElement = this; newChild.parentNode = this; + newChild.ownerDocument = this.ownerDocument; return newChild; } diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 6e02577f3a..536641b075 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -1,15 +1,29 @@ /** * @jest-environment jsdom */ -import { getDefaultSN, RRDocument, RRMediaElement } from '../src'; -import { createOrGetNode, diff, ReplayerHandler } from '../src/diff'; +import * as path from 'path'; +import * as puppeteer from 'puppeteer'; import { NodeType as RRNodeType, serializedNodeWithId, createMirror, - Mirror, + Mirror as NodeMirror, } from 'rrweb-snapshot'; -import type { IRRNode } from '../src/document'; +import { + buildFromDom, + getDefaultSN, + Mirror as RRNodeMirror, + RRDocument, + RRMediaElement, +} from '../src'; +import { + createOrGetNode, + diff, + ReplayerHandler, + nodeMatching, + sameNodeType, +} from '../src/diff'; +import type { IRRElement, IRRNode } from '../src/document'; import { Replayer } from 'rrweb'; import type { eventWithTime, @@ -18,6 +32,7 @@ import type { styleSheetRuleData, } from '@rrweb/types'; import { EventType, IncrementalSource } from '@rrweb/types'; +import { compileTSCode } from './utils'; const elementSn = { type: RRNodeType.Element, @@ -45,7 +60,7 @@ type RRNode = IRRNode; function createTree( treeNode: ElementType, rrDocument?: RRDocument, - mirror: Mirror = createMirror(), + mirror: NodeMirror = createMirror(), ): Node | RRNode { type TNode = typeof rrDocument extends RRDocument ? RRNode : Node; let root: TNode; @@ -87,7 +102,7 @@ function shuffle(list: number[]) { } describe('diff algorithm for rrdom', () => { - let mirror: Mirror; + let mirror: NodeMirror; let replayer: ReplayerHandler; beforeEach(() => { @@ -98,7 +113,9 @@ describe('diff algorithm for rrdom', () => { applyInput: () => {}, applyScroll: () => {}, applyStyleSheetMutation: () => {}, + afterAppend: () => {}, }; + document.write(''); }); describe('diff single node', () => { @@ -117,7 +134,7 @@ describe('diff algorithm for rrdom', () => { x: 0, y: 0, }; - replayer.applyScroll = jest.fn(); + const applyScrollFn = jest.spyOn(replayer, 'applyScroll'); diff(document, rrNode, replayer); expect(document.childNodes.length).toEqual(1); expect(document.childNodes[0]).toBeInstanceOf(DocumentType); @@ -126,7 +143,24 @@ describe('diff algorithm for rrdom', () => { '-//W3C//DTD XHTML 1.0 Transitional//EN', ); expect(document.doctype?.systemId).toEqual(''); - expect(replayer.applyScroll).toBeCalledTimes(1); + expect(applyScrollFn).toHaveBeenCalledTimes(1); + applyScrollFn.mockRestore(); + }); + + it('should apply scroll data on an element', () => { + const element = document.createElement('div'); + const rrDocument = new RRDocument(); + const rrNode = rrDocument.createElement('div'); + rrNode.scrollData = { + source: IncrementalSource.Scroll, + id: 0, + x: 0, + y: 0, + }; + const applyScrollFn = jest.spyOn(replayer, 'applyScroll'); + diff(element, rrNode, replayer); + expect(applyScrollFn).toHaveBeenCalledTimes(1); + applyScrollFn.mockRestore(); }); it('should apply input data on an input element', () => { @@ -247,6 +281,29 @@ describe('diff algorithm for rrdom', () => { expect(element.paused).toEqual(true); } }); + + it('should diff a node with different node type', () => { + // When the diff target has a different node type. + let parentNode: Node = document.createElement('div'); + let unreliableNode: Node = document.createTextNode(''); + parentNode.appendChild(unreliableNode); + const rrNode = new RRDocument().createElement('li'); + diff(unreliableNode, rrNode, replayer); + expect(parentNode.childNodes.length).toEqual(1); + expect(parentNode.childNodes[0]).toBeInstanceOf(HTMLElement); + expect((parentNode.childNodes[0] as HTMLElement).tagName).toEqual('LI'); + + // When the diff target has the same node type but with different tagName. + parentNode = document.createElement('div'); + unreliableNode = document.createElement('span'); + parentNode.appendChild(unreliableNode); + diff(unreliableNode, rrNode, replayer); + expect((parentNode.childNodes[0] as HTMLElement).tagName).toEqual('LI'); + + // When the diff target is a node without parentNode. + unreliableNode = document.createComment(''); + diff(unreliableNode, rrNode, replayer); + }); }); describe('diff properties', () => { @@ -1001,6 +1058,67 @@ describe('diff algorithm for rrdom', () => { newElementsIds, ); }); + + it('should diff children with unreliable Mirror', () => { + const parentNode = createTree( + { + tagName: 'div', + id: 0, + children: [], + }, + undefined, + mirror, + ) as Node; + // Construct unreliable Mirror data. + const unreliableChild = document.createTextNode(''); + const unreliableSN = { + id: 1, + textContent: '', + type: RRNodeType.Text, + } as serializedNodeWithId; + mirror.add(unreliableChild, unreliableSN); + parentNode.appendChild(unreliableChild); + createTree( + { + tagName: 'div', + id: 2, + children: [], + }, + undefined, + mirror, + ); + + const rrParentNode = createTree( + { + tagName: 'div', + id: 0, + children: [1].map((c) => ({ + tagName: 'span', + id: c, + children: [2].map((c1) => ({ + tagName: 'li', + id: c1, + })), + })), + }, + new RRDocument(), + ) as RRNode; + const id = 'correctElement'; + (rrParentNode.childNodes[0] as IRRElement).setAttribute('id', id); + diff(parentNode, rrParentNode, replayer); + + expect(parentNode.childNodes.length).toEqual(1); + expect(parentNode.childNodes[0]).toBeInstanceOf(HTMLElement); + + const spanChild = parentNode.childNodes[0] as HTMLElement; + expect(spanChild.tagName).toEqual('SPAN'); + expect(spanChild.id).toEqual(id); + expect(spanChild.childNodes.length).toEqual(1); + expect(spanChild.childNodes[0]).toBeInstanceOf(HTMLElement); + + const liChild = spanChild.childNodes[0] as HTMLElement; + expect(liChild.tagName).toEqual('LI'); + }); }); describe('diff shadow dom', () => { @@ -1040,6 +1158,8 @@ describe('diff algorithm for rrdom', () => { }); describe('diff iframe elements', () => { + jest.setTimeout(60_000); + it('should add an element to the contentDocument of an iframe element', () => { document.write(''); const node = document.createElement('iframe'); @@ -1191,6 +1311,306 @@ describe('diff algorithm for rrdom', () => { expect(element.nodeType).toBe(element.DOCUMENT_TYPE_NODE); expect(mirror.getId(element)).toEqual(-1); }); + + it('should remove children from document before adding new nodes 4', () => { + /** + * This case aims to test whether the diff function can remove all the old doctype and html element from the document before adding new doctype and html element. + * If not, the diff function will throw errors or warnings. + */ + // Mock the original console.warn function to make the test fail once console.warn is called. + const warn = jest.spyOn(global.console, 'warn'); + + document.write(''); + const rrdom = new RRDocument(); + /** + * Make the structure of document and RRDom look like this: + * -2 Document + * -3 DocumentType + * -4 HTML + * -5 HEAD + * -6 BODY + */ + buildFromDom(document, mirror, rrdom); + expect(mirror.getId(document)).toBe(-2); + expect(mirror.getId(document.body)).toBe(-6); + expect(rrdom.mirror.getId(rrdom)).toBe(-2); + expect(rrdom.mirror.getId(rrdom.body)).toBe(-6); + + rrdom.childNodes = []; + /** + * Rebuild the rrdom and make it looks like this: + * -7 RRDocument + * -8 RRDocumentType + * -9 HTML + * -10 HEAD + * -11 BODY + */ + buildFromDom(document, undefined, rrdom); + // Keep the ids of real document unchanged. + expect(mirror.getId(document)).toBe(-2); + expect(mirror.getId(document.body)).toBe(-6); + + expect(rrdom.mirror.getId(rrdom)).toBe(-7); + expect(rrdom.mirror.getId(rrdom.body)).toBe(-11); + + // Diff the document with the new rrdom. + diff(document, rrdom, replayer); + // Check that warn was not called (fail on warning) + expect(warn).not.toHaveBeenCalled(); + + // Check that the old nodes are removed from the NodeMirror. + [-2, -3, -4, -5, -6].forEach((id) => + expect(mirror.getNode(id)).toBeNull(), + ); + expect(mirror.getId(document)).toBe(-7); + expect(mirror.getId(document.doctype)).toBe(-8); + expect(mirror.getId(document.documentElement)).toBe(-9); + expect(mirror.getId(document.head)).toBe(-10); + expect(mirror.getId(document.body)).toBe(-11); + + warn.mockRestore(); + }); + + it('selectors should be case-sensitive for matching in iframe dom', async () => { + /** + * If the selector match is case insensitive, it will cause some CSS style problems in the replayer. + * This test result executed in JSDom is different from that in real browser so we use puppeteer as test environment. + */ + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto('about:blank'); + + try { + const code = await compileTSCode( + path.resolve(__dirname, '../src/index.ts'), + ); + await page.evaluate(code); + + const className = 'case-sensitive'; + // To show the selector match pattern (case sensitive) in normal dom. + const caseInsensitiveInNormalDom = await page.evaluate((className) => { + document.write( + '', + ); + const htmlEl = document.documentElement; + htmlEl.className = className.toLowerCase(); + return htmlEl.matches(`.${className.toUpperCase()}`); + }, className); + expect(caseInsensitiveInNormalDom).toBeFalsy(); + + // To show the selector match pattern (case insensitive) in auto mounted iframe dom. + const caseInsensitiveInDefaultIFrameDom = await page.evaluate( + (className) => { + const iframeEl = document.querySelector('iframe'); + const htmlEl = iframeEl?.contentDocument?.documentElement; + if (htmlEl) { + htmlEl.className = className.toLowerCase(); + return htmlEl.matches(`.${className.toUpperCase()}`); + } + }, + className, + ); + expect(caseInsensitiveInDefaultIFrameDom).toBeTruthy(); + + const iframeElId = 3, + iframeDomId = 4, + htmlElId = 5; + const result = await page.evaluate(` + const iframeEl = document.querySelector('iframe'); + + // Construct a virtual dom tree. + const rrDocument = new rrdom.RRDocument(); + const rrIframeEl = rrDocument.createElement('iframe'); + rrDocument.mirror.add(rrIframeEl, rrdom.getDefaultSN(rrIframeEl, ${iframeElId})); + rrDocument.appendChild(rrIframeEl); + rrDocument.mirror.add( + rrIframeEl.contentDocument, + rrdom.getDefaultSN(rrIframeEl.contentDocument, ${iframeDomId}), + ); + const rrDocType = rrDocument.createDocumentType('html', '', ''); + rrIframeEl.contentDocument.appendChild(rrDocType); + const rrHtmlEl = rrDocument.createElement('html'); + rrDocument.mirror.add(rrHtmlEl, rrdom.getDefaultSN(rrHtmlEl, ${htmlElId})); + rrIframeEl.contentDocument.appendChild(rrHtmlEl); + + const replayer = { + mirror: rrdom.createMirror(), + applyCanvas: () => {}, + applyInput: () => {}, + applyScroll: () => {}, + applyStyleSheetMutation: () => {}, + }; + rrdom.diff(iframeEl, rrIframeEl, replayer); + + iframeEl.contentDocument.documentElement.className = + '${className.toLowerCase()}'; + iframeEl.contentDocument.childNodes.length === 2 && + replayer.mirror.getId(iframeEl.contentDocument.documentElement) === ${htmlElId} && + // To test whether the selector match of the updated iframe document is case sensitive or not. + !iframeEl.contentDocument.documentElement.matches( + '.${className.toUpperCase()}', + ); + `); + // IFrame document has two children, mirror id of documentElement is ${htmlElId}, and selectors should be case-sensitive for matching in iframe dom (consistent with the normal dom). + expect(result).toBeTruthy(); + } finally { + await page.close(); + await browser.close(); + } + }); + }); + + describe('afterAppend callback', () => { + it('should call afterAppend callback', () => { + const afterAppendFn = jest.spyOn(replayer, 'afterAppend'); + const node = createTree( + { + tagName: 'div', + id: 1, + }, + undefined, + mirror, + ) as Node; + + const rrdom = new RRDocument(); + const rrNode = createTree( + { + tagName: 'div', + id: 1, + children: [ + { + tagName: 'span', + id: 2, + }, + ], + }, + rrdom, + ) as RRNode; + diff(node, rrNode, replayer); + expect(afterAppendFn).toHaveBeenCalledTimes(1); + expect(afterAppendFn).toHaveBeenCalledWith(node.childNodes[0], 2); + afterAppendFn.mockRestore(); + }); + + it('should diff without afterAppend callback', () => { + replayer.afterAppend = undefined; + const rrdom = buildFromDom(document); + document.open(); + diff(document, rrdom, replayer); + replayer.afterAppend = () => {}; + }); + + it('should call afterAppend callback in the post traversal order', () => { + const afterAppendFn = jest.spyOn(replayer, 'afterAppend'); + document.open(); + + const rrdom = new RRDocument(); + rrdom.mirror.add(rrdom, getDefaultSN(rrdom, 1)); + const rrNode = createTree( + { + tagName: 'html', + id: 1, + children: [ + { + tagName: 'head', + id: 2, + }, + { + tagName: 'body', + id: 3, + children: [ + { + tagName: 'span', + id: 4, + children: [ + { + tagName: 'li', + id: 5, + }, + { + tagName: 'li', + id: 6, + }, + ], + }, + { + tagName: 'p', + id: 7, + }, + { + tagName: 'p', + id: 8, + children: [ + { + tagName: 'li', + id: 9, + }, + { + tagName: 'li', + id: 10, + }, + ], + }, + ], + }, + ], + }, + rrdom, + ) as RRNode; + diff(document, rrNode, replayer); + + expect(afterAppendFn).toHaveBeenCalledTimes(10); + // the correct traversal order + [2, 5, 6, 4, 7, 9, 10, 8, 3, 1].forEach((id, index) => { + expect((mirror.getNode(id) as HTMLElement).tagName).toEqual( + (rrdom.mirror.getNode(id) as IRRElement).tagName, + ); + expect(afterAppendFn).toHaveBeenNthCalledWith( + index + 1, + mirror.getNode(id), + id, + ); + }); + }); + + it('should only call afterAppend for newly created nodes', () => { + const afterAppendFn = jest.spyOn(replayer, 'afterAppend'); + const rrdom = buildFromDom(document, replayer.mirror) as RRDocument; + + // Append 3 nodes to rrdom. + const rrNode = createTree( + { + tagName: 'span', + id: 1, + children: [ + { + tagName: 'li', + id: 2, + }, + { + tagName: 'li', + id: 3, + }, + ], + }, + rrdom, + ) as RRNode; + rrdom.body?.appendChild(rrNode); + diff(document, rrdom, replayer); + expect(afterAppendFn).toHaveBeenCalledTimes(3); + // Should only call afterAppend for 3 newly appended nodes. + [2, 3, 1].forEach((id, index) => { + expect((mirror.getNode(id) as HTMLElement).tagName).toEqual( + (rrdom.mirror.getNode(id) as IRRElement).tagName, + ); + expect(afterAppendFn).toHaveBeenNthCalledWith( + index + 1, + mirror.getNode(id), + id, + ); + }); + afterAppendFn.mockClear(); + }); }); describe('create or get a Node', () => { @@ -1431,4 +1851,116 @@ describe('diff algorithm for rrdom', () => { ).toEqual('a {color: blue;}'); }); }); + + describe('test sameNodeType function', () => { + const rrdom = new RRDocument(); + it('should return true when two elements have same tagNames', () => { + const div1 = document.createElement('div'); + const div2 = rrdom.createElement('div'); + expect(sameNodeType(div1, div2)).toBeTruthy(); + }); + + it('should return false when two elements have different tagNames', () => { + const div1 = document.createElement('div'); + const div2 = rrdom.createElement('span'); + expect(sameNodeType(div1, div2)).toBeFalsy(); + }); + + it('should return false when two nodes have the same node type', () => { + let node1: Node = new Document(); + let node2: IRRNode = new RRDocument(); + expect(sameNodeType(node1, node2)).toBeTruthy(); + + node1 = document.implementation.createDocumentType('html', '', ''); + node2 = rrdom.createDocumentType('', '', ''); + expect(sameNodeType(node1, node2)).toBeTruthy(); + + node1 = document.createTextNode('node1'); + node2 = rrdom.createTextNode('node2'); + expect(sameNodeType(node1, node2)).toBeTruthy(); + + node1 = document.createComment('node1'); + node2 = rrdom.createComment('node2'); + expect(sameNodeType(node1, node2)).toBeTruthy(); + }); + + it('should return false when two nodes have different node types', () => { + let node1: Node = new Document(); + let node2: IRRNode = rrdom.createDocumentType('', '', ''); + expect(sameNodeType(node1, node2)).toBeFalsy(); + + node1 = document.implementation.createDocumentType('html', '', ''); + node2 = new RRDocument(); + expect(sameNodeType(node1, node2)).toBeFalsy(); + + node1 = document.createTextNode('node1'); + node2 = rrdom.createComment('node2'); + expect(sameNodeType(node1, node2)).toBeFalsy(); + + node1 = document.createComment('node1'); + node2 = rrdom.createTextNode('node2'); + expect(sameNodeType(node1, node2)).toBeFalsy(); + }); + }); + + describe('test nodeMatching function', () => { + const rrdom = new RRDocument(); + const NodeMirror = createMirror(); + const rrdomMirror = new RRNodeMirror(); + beforeEach(() => { + NodeMirror.reset(); + rrdomMirror.reset(); + }); + + it('should return false when two nodes have different Ids', () => { + const node1 = document.createElement('div'); + const node2 = rrdom.createElement('div'); + NodeMirror.add(node1, getDefaultSN(node2, 1)); + rrdomMirror.add(node2, getDefaultSN(node2, 2)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + }); + + it('should return false when two nodes have same Ids but different node types', () => { + // Compare an element with a comment node + let node1: Node = document.createElement('div'); + NodeMirror.add(node1, getDefaultSN(rrdom.createElement('div'), 1)); + let node2: IRRNode = rrdom.createComment('test'); + rrdomMirror.add(node2, getDefaultSN(node2, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + + // Compare an element node with a text node + node2 = rrdom.createTextNode(''); + rrdomMirror.add(node2, getDefaultSN(node2, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + + // Compare a document with a text node + node1 = new Document(); + NodeMirror.add(node1, getDefaultSN(rrdom, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + + // Compare a document with a document type node + node2 = rrdom.createDocumentType('', '', ''); + rrdomMirror.add(node2, getDefaultSN(node2, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + }); + + it('should compare two elements', () => { + // Compare two elements with different tagNames + let node1 = document.createElement('div'); + let node2 = rrdom.createElement('span'); + NodeMirror.add(node1, getDefaultSN(rrdom.createElement('div'), 1)); + rrdomMirror.add(node2, getDefaultSN(node2, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + + // Compare two elements with same tagNames but different attributes + node2 = rrdom.createElement('div'); + node2.setAttribute('class', 'test'); + rrdomMirror.add(node2, getDefaultSN(node2, 1)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeTruthy(); + + // Should return false when two elements have same tagNames and attributes but different children + rrdomMirror.add(node2, getDefaultSN(node2, 2)); + expect(nodeMatching(node1, node2, NodeMirror, rrdomMirror)).toBeFalsy(); + }); + }); }); diff --git a/packages/rrdom/test/document.test.ts b/packages/rrdom/test/document.test.ts index c9ee13c3a0..47d9fa73e8 100644 --- a/packages/rrdom/test/document.test.ts +++ b/packages/rrdom/test/document.test.ts @@ -133,7 +133,7 @@ describe('Basic RRDocument implementation', () => { expect(node.parentElement).toEqual(null); expect(node.childNodes).toBeInstanceOf(Array); expect(node.childNodes.length).toBe(0); - expect(node.ownerDocument).toBeUndefined(); + expect(node.ownerDocument).toBe(node); expect(node.textContent).toBeNull(); expect(node.RRNodeType).toBe(RRNodeType.Document); expect(node.nodeType).toBe(document.nodeType); diff --git a/packages/rrdom/test/utils.ts b/packages/rrdom/test/utils.ts new file mode 100644 index 0000000000..a189ae365a --- /dev/null +++ b/packages/rrdom/test/utils.ts @@ -0,0 +1,26 @@ +import * as rollup from 'rollup'; +import * as typescript from 'rollup-plugin-typescript2'; +import resolve from '@rollup/plugin-node-resolve'; +const _typescript = (typescript as unknown) as typeof typescript.default; + +/** + * Use rollup to compile an input TS script into JS code string. + */ +export async function compileTSCode(inputFilePath: string) { + const bundle = await rollup.rollup({ + input: inputFilePath, + plugins: [ + (resolve() as unknown) as rollup.Plugin, + (_typescript({ + tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, + }) as unknown) as rollup.Plugin, + ], + }); + const { + output: [{ code: _code }], + } = await bundle.generate({ + name: 'rrdom', + format: 'iife', + }); + return _code; +} diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index e414e230e8..0b25f684b2 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -4,9 +4,6 @@ import * as fs from 'fs'; import * as path from 'path'; import * as puppeteer from 'puppeteer'; -import * as rollup from 'rollup'; -import resolve from '@rollup/plugin-node-resolve'; -import * as typescript from 'rollup-plugin-typescript2'; import { JSDOM } from 'jsdom'; import { cdataNode, @@ -29,8 +26,8 @@ import { RRElement, BaseRRNode as RRNode, } from '../src'; +import { compileTSCode } from './utils'; -const _typescript = (typescript as unknown) as typeof typescript.default; const printRRDomCode = ` /** * Print the RRDom as a string. @@ -61,7 +58,7 @@ describe('RRDocument for browser environment', () => { describe('create a RRNode from a real Node', () => { it('should support quicksmode documents', () => { - // seperate jsdom document as changes to the document would otherwise bleed into other tests + // separate jsdom document as changes to the document would otherwise bleed into other tests const dom = new JSDOM(); const document = dom.window.document; @@ -219,22 +216,7 @@ describe('RRDocument for browser environment', () => { beforeAll(async () => { browser = await puppeteer.launch(); - const bundle = await rollup.rollup({ - input: path.resolve(__dirname, '../src/index.ts'), - plugins: [ - (resolve() as unknown) as rollup.Plugin, - (_typescript({ - tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, - }) as unknown) as rollup.Plugin, - ], - }); - const { - output: [{ code: _code }], - } = await bundle.generate({ - name: 'rrdom', - format: 'iife', - }); - code = _code; + code = await compileTSCode(path.resolve(__dirname, '../src/index.ts')); }); afterAll(async () => { await browser.close(); diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 0e2abb119f..3cd1567e6a 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -230,14 +230,24 @@ export class Replayer { else if (data.source === IncrementalSource.StyleDeclaration) this.applyStyleDeclaration(data, styleSheet); }, + afterAppend: (node: Node, id: number) => { + for (const plugin of this.config.plugins || []) { + if (plugin.onBuild) plugin.onBuild(node, { id, replayer: this }); + } + }, }; - this.iframe.contentDocument && - diff( - this.iframe.contentDocument, - this.virtualDom, - replayerHandler, - this.virtualDom.mirror, - ); + if (this.iframe.contentDocument) + try { + diff( + this.iframe.contentDocument, + this.virtualDom, + replayerHandler, + this.virtualDom.mirror, + ); + } catch (e) { + console.warn(e); + } + this.virtualDom.destroyTree(); this.usingVirtualDom = false; @@ -858,6 +868,8 @@ export class Replayer { ); } + // Skip the plugin onBuild callback in the virtual dom mode + if (this.usingVirtualDom) return; for (const plugin of this.config.plugins || []) { if (plugin.onBuild) plugin.onBuild(builtNode, { @@ -1475,6 +1487,8 @@ export class Replayer { return; } const afterAppend = (node: Node | RRNode, id: number) => { + // Skip the plugin onBuild callback for virtual dom + if (this.usingVirtualDom) return; for (const plugin of this.config.plugins || []) { if (plugin.onBuild) plugin.onBuild(node, { id, replayer: this }); } From 07d22e7cd999a48e7371aaef1b979574bb746500 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 20:14:33 +1100 Subject: [PATCH 119/201] enable to customize logger in the replayer (#1111) --- guide.md | 1 + guide.zh_CN.md | 1 + packages/rrweb/src/replay/index.ts | 47 ++++++++++++------------------ packages/rrweb/src/types.ts | 4 +++ 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/guide.md b/guide.md index 76f781d096..8a2ddb820f 100644 --- a/guide.md +++ b/guide.md @@ -311,6 +311,7 @@ The replayer accepts options as its constructor's second parameter, and it has t | logConfig | - | configuration of console output playback, refer to the [console recipe](./docs/recipes/console.md) | | plugins | [] | load plugins to provide extended replay functions. [What is plugins?](./docs/recipes/plugin.md) | | useVirtualDom | true | whether to use Virtual Dom optimization in the process of skipping to a new point of time | +| logger | console | The logger object used by the replayer to print warnings or errors | #### Use rrweb-player diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 1093dbb386..ddb9038313 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -306,6 +306,7 @@ replayer.destroy(); | unpackFn | - | 数据解压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | | plugins | [] | 加载插件以获得额外的回放功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | | useVirtualDom | true | 在播放器跳转到一个新的时间点的过程中,是否使用 Virtual Dom 优化 | +| logger | console | 当播放器出现警告或错误时用来打印日志的对象 | #### 使用 rrweb-player diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 3cd1567e6a..edb2b9cb9b 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -184,6 +184,7 @@ export class Replayer { pauseAnimation: true, mouseTail: defaultMouseTailConfig, useVirtualDom: true, // Virtual-dom optimization is enabled by default. + logger: console, }; this.config = Object.assign({}, defaultConfig, config); @@ -269,9 +270,7 @@ export class Replayer { ); value.node = realNode; } catch (error) { - if (this.config.showWarning) { - console.warn(error); - } + this.warn(error); } } } @@ -495,7 +494,7 @@ export class Replayer { } public resume(timeOffset = 0) { - console.warn( + this.warn( `The 'resume' was deprecated in 1.0. Please use 'play' method which has the same interface.`, ); this.play(timeOffset); @@ -753,10 +752,10 @@ export class Replayer { isSync = false, ) { if (!this.iframe.contentDocument) { - return console.warn('Looks like your replayer has been destroyed.'); + return this.warn('Looks like your replayer has been destroyed.'); } if (Object.keys(this.legacy_missingNodeRetryMap).length) { - console.warn( + this.warn( 'Found unresolved missing node map', this.legacy_missingNodeRetryMap, ); @@ -1252,12 +1251,10 @@ export class Replayer { mediaEl.playbackRate = d.playbackRate; } } catch (error) { - if (this.config.showWarning) { - console.warn( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-template-expressions - `Failed to replay media interactions: ${error.message || 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; } @@ -1314,9 +1311,7 @@ export class Replayer { ); this.iframe.contentDocument?.fonts.add(fontFace); } catch (error) { - if (this.config.showWarning) { - console.warn(error); - } + this.warn(error); } break; } @@ -1354,9 +1349,7 @@ export class Replayer { ); if (virtualNode) value.node = virtualNode; } catch (error) { - if (this.config.showWarning) { - console.warn(error); - } + this.warn(error); } } } @@ -1437,7 +1430,7 @@ export class Replayer { const appendNode = (mutation: addedNodeMutation) => { if (!this.iframe.contentDocument) { - return console.warn('Looks like your replayer has been destroyed.'); + return this.warn('Looks like your replayer has been destroyed.'); } let parent: Node | null | ShadowRoot | RRNode = mirror.getNode( mutation.parentId, @@ -1718,12 +1711,10 @@ export class Replayer { 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; @@ -2136,20 +2127,20 @@ export class Replayer { * 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; } - console.log(REPLAY_CONSOLE_PREFIX, ...args); + this.config.logger.log(REPLAY_CONSOLE_PREFIX, ...args); } } diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index fb3a300b40..e2cde98f8a 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -180,6 +180,10 @@ export type playerConfig = { }; unpackFn?: UnpackFn; useVirtualDom: boolean; + logger: { + log: (...args: Parameters) => void; + warn: (...args: Parameters) => void; + }; plugins?: ReplayPlugin[]; }; From 0627d4df7cc76cde7babbd37ab8e3da5810fb51d Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 20:14:49 +1100 Subject: [PATCH 120/201] add option to record on DOMContentLoaded event (#1109) --- guide.md | 1 + guide.zh_CN.md | 1 + packages/rrweb/src/record/index.ts | 26 +++--- packages/rrweb/src/types.ts | 1 + .../__snapshots__/integration.test.ts.snap | 83 +++++++++++++++++++ packages/rrweb/test/integration.test.ts | 15 ++++ packages/rrweb/test/utils.ts | 1 + 7 files changed, 117 insertions(+), 11 deletions(-) diff --git a/guide.md b/guide.md index 8a2ddb820f..383a473275 100644 --- a/guide.md +++ b/guide.md @@ -158,6 +158,7 @@ The parameter of `rrweb.record` accepts the following options. | sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | | recordCanvas | false | Whether to record the canvas element. Available options:
`false`,
`true` | | recordCrossOriginIframes | false | Whether to record cross origin iframes. rrweb has to be injected in each child iframe for this to work. Available options:
`false`,
`true` | +| recordAfter | 'load' | If the document is not ready, then the recorder will start recording after the specified event is fired. Available options: `DOMContentLoaded`, `load` | | inlineImages | false | whether to record the image content | | collectFonts | false | whether to collect fonts in the website | | userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) | diff --git a/guide.zh_CN.md b/guide.zh_CN.md index ddb9038313..8e6f2c5a2b 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -154,6 +154,7 @@ setInterval(save, 10 * 1000); | dataURLOptions | {} | Canvas 图像快照的格式和质量,这个参数将传递给 OffscreenCanvas.convertToBlob(),使用这个参数能有效减小录制数据的大小 | | recordCanvas | false | 是否记录 canvas 内容, 可用选项:`false`, `true` | | recordCrossOriginIframes | false | 是否记录 cross origin iframes。 必须在每个子 iframe 中注入 rrweb 才能使其工作。 可用选项:`false`, `true` | +| recordAfter | 'load' | 如果 document 还没有加载完成,recorder 将会在指定的事件触发后开始录制。可用选项: `DOMContentLoaded`, `load` | | inlineImages | false | 是否将图片内容记内联录制 | | collectFonts | false | 是否记录页面中的字体文件 | | userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index c28ec28a2c..6c79007690 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -76,6 +76,9 @@ function record( mousemoveWait, recordCanvas = false, recordCrossOriginIframes = false, + recordAfter = options.recordAfter === 'DOMContentLoaded' + ? options.recordAfter + : 'load', userTriggeredOnInput = false, collectFonts = false, inlineImages = false, @@ -408,16 +411,6 @@ function record( try { const handlers: listenerHandler[] = []; - handlers.push( - on('DOMContentLoaded', () => { - wrappedEmit( - wrapEvent({ - type: EventType.DomContentLoaded, - data: {}, - }), - ); - }), - ); const observe = (doc: Document) => { return initObservers( @@ -583,6 +576,17 @@ function record( ) { init(); } else { + handlers.push( + on('DOMContentLoaded', () => { + wrappedEmit( + wrapEvent({ + type: EventType.DomContentLoaded, + data: {}, + }), + ); + if (recordAfter === 'DOMContentLoaded') init(); + }), + ); handlers.push( on( 'load', @@ -593,7 +597,7 @@ function record( data: {}, }), ); - init(); + if (recordAfter === 'load') init(); }, window, ), diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index e2cde98f8a..c1f5f0d97a 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -61,6 +61,7 @@ export type recordOptions = { dataURLOptions?: DataURLOptions; recordCanvas?: boolean; recordCrossOriginIframes?: boolean; + recordAfter?: 'DOMContentLoaded' | 'load'; userTriggeredOnInput?: boolean; collectFonts?: boolean; inlineImages?: boolean; diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 631580b1aa..ea037dff38 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -8408,6 +8408,89 @@ exports[`record integration tests should record DOM node movement 2 1`] = ` ]" `; +exports[`record integration tests should record after DOMContentLoaded event 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 3 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 7 + } + ], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 8 + } + ], + \\"id\\": 4 + } + ], + \\"id\\": 2 + } + ], + \\"compatMode\\": \\"BackCompat\\", + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 1, + \\"data\\": {} + } +]" +`; + exports[`record integration tests should record canvas mutations 1`] = ` "[ { diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 516d3a289a..0371914fe6 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -984,4 +984,19 @@ describe('record integration tests', function (this: ISuite) { )) as eventWithTime[]; assertSnapshot(snapshots); }); + + it('should record after DOMContentLoaded event', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml.call(this, 'blank.html', { + recordAfter: 'DOMContentLoaded', + }), + ); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 1d9b7a861e..0ece25e5d0 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -601,6 +601,7 @@ export function generateRecordSnippet(options: recordOptions) { userTriggeredOnInput: ${options.userTriggeredOnInput}, maskTextFn: ${options.maskTextFn}, recordCanvas: ${options.recordCanvas}, + recordAfter: '${options.recordAfter || 'load'}', inlineImages: ${options.inlineImages}, plugins: ${options.plugins} }); From 174b9ac066565b8c065f40f0303189f10c7c4efb Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Thu, 9 Feb 2023 23:21:45 +1100 Subject: [PATCH 121/201] fix: mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed (#1112) Co-authored-by: Justin Halsall --- packages/rrweb/src/replay/index.ts | 33 +++-- .../rrweb/test/events/document-replacement.ts | 121 ++++++++++++++++++ packages/rrweb/test/replayer.test.ts | 22 ++++ 3 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 packages/rrweb/test/events/document-replacement.ts diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index edb2b9cb9b..1b1f6b44fa 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1532,6 +1532,30 @@ export class Replayer { parent.removeChild(c as Node & RRNode); } } + } 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 Document | 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] as Node & RRNode); + /** + * 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 as HTMLElement & RRNode, + ); } if (previous && previous.nextSibling && previous.nextSibling.parentNode) { @@ -1546,15 +1570,6 @@ export class Replayer { ? (parent as TNode).insertBefore(target as TNode, next as TNode) : (parent as TNode).insertBefore(target as TNode, 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 as TNode).removeChild(targetDoc.firstChild as TNode); - } - } - (parent as TNode).appendChild(target as TNode); } /** diff --git a/packages/rrweb/test/events/document-replacement.ts b/packages/rrweb/test/events/document-replacement.ts new file mode 100644 index 0000000000..8b79e7dc9b --- /dev/null +++ b/packages/rrweb/test/events/document-replacement.ts @@ -0,0 +1,121 @@ +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: [], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + type: EventType.FullSnapshot, + timestamp: now + 100, + }, + // mutation that replace the old document + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [], + removes: [], + adds: [ + { + parentId: 1, + nextId: null, + node: { + type: 2, + tagName: 'html', + attributes: {}, + childNodes: [], + id: 6, + }, + }, + { + parentId: 6, + nextId: null, + node: { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [], + id: 7, + }, + }, + { + parentId: 1, + nextId: 6, + node: { + type: 1, + name: 'html', + publicId: '', + systemId: '', + id: 8, + }, + }, + { + parentId: 6, + nextId: 7, + node: { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + id: 9, + }, + }, + ], + }, + timestamp: now + 500, + }, +]; + +export default events; diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 900f0eeb16..71b85956ea 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -20,6 +20,7 @@ import StyleSheetTextMutation from './events/style-sheet-text-mutation'; import canvasInIframe from './events/canvas-in-iframe'; import adoptedStyleSheet from './events/adopted-style-sheet'; import adoptedStyleSheetModification from './events/adopted-style-sheet-modification'; +import documentReplacementEvents from './events/document-replacement'; import { ReplayerEvents } from '@rrweb/types'; interface ISuite { @@ -991,4 +992,25 @@ describe('replayer', function () { await page.evaluate('replayer.pause(630);'); await check600ms(); }); + + it('should replay document replacement events without warnings or errors', async () => { + await page.evaluate( + `events = ${JSON.stringify(documentReplacementEvents)}`, + ); + const warningThrown = jest.fn(); + page.on('console', warningThrown); + const errorThrown = jest.fn(); + page.on('pageerror', errorThrown); + await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.play(500); + `); + await waitForRAF(page); + + // No warnings should be logged. + expect(warningThrown).not.toHaveBeenCalled(); + // No errors should be thrown. + expect(errorThrown).not.toHaveBeenCalled(); + }); }); From 5a1e5e919e3f8bef48d142115c0afd5706a442b5 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sat, 11 Feb 2023 10:19:52 +0100 Subject: [PATCH 122/201] Automate NPM package releases (#1119) --- .changeset/README.md | 8 + .changeset/config.json | 11 + .changeset/pre.json | 13 + .github/workflows/release.yml | 38 + package.json | 4 +- packages/rrdom-nodejs/src/document-nodejs.ts | 26 +- packages/rrdom-nodejs/src/polyfill.ts | 10 +- packages/rrdom-nodejs/test/polyfill.test.ts | 14 +- packages/rrdom/src/diff.ts | 7 +- packages/rrdom/src/document.ts | 24 +- packages/rrdom/src/index.ts | 5 +- packages/rrdom/test/diff.test.ts | 199 ++-- packages/rrdom/test/document.test.ts | 2 +- packages/rrdom/test/utils.ts | 8 +- packages/rrdom/test/virtual-dom.test.ts | 4 +- packages/rrweb-snapshot/src/rebuild.ts | 4 +- packages/rrweb-snapshot/src/utils.ts | 2 +- .../rrweb-snapshot/test/integration.test.ts | 8 +- .../rrweb/src/plugins/console/record/index.ts | 18 +- .../src/plugins/console/record/stringify.ts | 2 +- .../rrweb/src/plugins/console/replay/index.ts | 10 +- .../src/plugins/sequential-id/replay/index.ts | 2 +- .../src/record/cross-origin-iframe-mirror.ts | 3 +- packages/rrweb/src/record/iframe-manager.ts | 12 +- packages/rrweb/src/record/index.ts | 4 +- packages/rrweb/src/record/mutation.ts | 48 +- packages/rrweb/src/record/observer.ts | 33 +- .../record/observers/canvas/canvas-manager.ts | 3 +- .../rrweb/src/record/shadow-dom-manager.ts | 10 +- packages/rrweb/src/replay/canvas/2d.ts | 2 +- packages/rrweb/src/replay/index.ts | 26 +- packages/rrweb/src/types.ts | 3 +- packages/rrweb/src/utils.ts | 10 +- .../test/events/style-sheet-rule-events.ts | 6 +- packages/rrweb/test/integration.test.ts | 7 +- packages/rrweb/test/packer.test.ts | 2 +- packages/rrweb/test/record.test.ts | 97 +- .../test/record/cross-origin-iframes.test.ts | 48 +- packages/rrweb/test/record/webgl.test.ts | 4 +- .../rrweb/test/replay/webgl-mutation.test.ts | 4 +- packages/rrweb/test/replayer.test.ts | 4 +- packages/rrweb/test/utils.ts | 13 +- yarn.lock | 934 +++++++++++++++++- 43 files changed, 1300 insertions(+), 392 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .changeset/pre.json create mode 100644 .github/workflows/release.yml diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000000..e5b6d8d6a6 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000000..d9d5734926 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..96dfc32c78 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,13 @@ +{ + "mode": "pre", + "tag": "alpha", + "initialVersions": { + "rrdom": "0.1.7", + "rrdom-nodejs": "0.1.7", + "rrweb": "2.0.0-alpha.4", + "rrweb-player": "1.0.0-alpha.4", + "rrweb-snapshot": "2.0.0-alpha.4", + "@rrweb/types": "2.0.0-alpha.4" + }, + "changesets": [] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..f5f4afccce --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release + +on: + push: + branches: + - master + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Setup Node.js lts/* + uses: actions/setup-node@v3 + with: + node-version: lts/* + + - name: Install Dependencies + run: yarn + + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@v1 + with: + publish: yarn run release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # - name: Send a Slack notification if a publish happens + # if: steps.changesets.outputs.published == 'true' + # # You can do something when a publish happens. + # run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!" diff --git a/package.json b/package.json index 7192130275..2245470f48 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "packages/*" ], "devDependencies": { + "@changesets/cli": "^2.26.0", "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", "@typescript-eslint/eslint-plugin": "^5.25.0", "@typescript-eslint/parser": "^5.25.0", @@ -42,7 +43,8 @@ "repl": "cd packages/rrweb && npm run repl", "live-stream": "cd packages/rrweb && yarn live-stream", "lint": "yarn run concurrently --success=all -r -m=1 'yarn run markdownlint docs' 'yarn eslint packages/*/src --ext .ts,.tsx,.js,.jsx,.svelte'", - "lint:report": "yarn eslint --output-file eslint_report.json --format json packages/*/src --ext .ts,.tsx,.js,.jsx" + "lint:report": "yarn eslint --output-file eslint_report.json --format json packages/*/src --ext .ts,.tsx,.js,.jsx", + "release": "yarn build:all && changeset publish" }, "resolutions": { "**/jsdom/cssom": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz" diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index 69f85b87f5..1d13970d34 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -36,15 +36,16 @@ export class RRWindow { export class RRDocument extends BaseRRDocumentImpl(RRNode) - implements IRRDocument { + implements IRRDocument +{ readonly nodeName: '#document' = '#document'; private _nwsapi: NWSAPI; get nwsapi() { if (!this._nwsapi) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call this._nwsapi = nwsapi({ - document: (this as unknown) as Document, - DOMException: (null as unknown) as new ( + document: this as unknown as Document, + DOMException: null as unknown as new ( message?: string, name?: string, ) => DOMException, @@ -97,7 +98,7 @@ export class RRDocument } querySelectorAll(selectors: string): RRNode[] { - return (this.nwsapi.select(selectors) as unknown) as RRNode[]; + return this.nwsapi.select(selectors) as unknown as RRNode[]; } getElementsByTagName(tagName: string): RRElement[] { @@ -220,7 +221,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore get style() { - return (this._style as unknown) as CSSStyleDeclaration; + return this._style as unknown as CSSStyleDeclaration; } attachShadow(_init: ShadowRootInit): RRElement { @@ -268,14 +269,14 @@ export class RRElement extends BaseRRElementImpl(RRNode) { querySelectorAll(selectors: string): RRNode[] { const result: RRElement[] = []; if (this.ownerDocument !== null) { - ((this.ownerDocument as RRDocument).nwsapi.select( + (this.ownerDocument as RRDocument).nwsapi.select( selectors, - (this as unknown) as Element, + this as unknown as Element, (element) => { - if (((element as unknown) as RRElement) !== this) - result.push((element as unknown) as RRElement); + if ((element as unknown as RRElement) !== this) + result.push(element as unknown as RRElement); }, - ) as unknown) as RRNode[]; + ) as unknown as RRNode[]; } return result; } @@ -393,6 +394,5 @@ interface RRElementTagNameMap { video: RRMediaElement; } -type RRElementType< - K extends keyof HTMLElementTagNameMap -> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; +type RRElementType = + K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; diff --git a/packages/rrdom-nodejs/src/polyfill.ts b/packages/rrdom-nodejs/src/polyfill.ts index ae1f88049b..af0dae0cc6 100644 --- a/packages/rrdom-nodejs/src/polyfill.ts +++ b/packages/rrdom-nodejs/src/polyfill.ts @@ -51,10 +51,10 @@ export function polyfillRAF() { } } - (global as Window & - typeof globalThis).requestAnimationFrame = requestAnimationFrame; - (global as Window & - typeof globalThis).cancelAnimationFrame = cancelAnimationFrame; + (global as Window & typeof globalThis).requestAnimationFrame = + requestAnimationFrame; + (global as Window & typeof globalThis).cancelAnimationFrame = + cancelAnimationFrame; } /** @@ -88,5 +88,5 @@ export function polyfillDocument() { rrdom.documentElement!.appendChild(rrdom.createElement('head')); rrdom.documentElement!.appendChild(rrdom.createElement('body')); })(); - global.document = (rrdom as unknown) as Document; + global.document = rrdom as unknown as Document; } diff --git a/packages/rrdom-nodejs/test/polyfill.test.ts b/packages/rrdom-nodejs/test/polyfill.test.ts index 240976ad83..bca249b046 100644 --- a/packages/rrdom-nodejs/test/polyfill.test.ts +++ b/packages/rrdom-nodejs/test/polyfill.test.ts @@ -26,7 +26,7 @@ describe('polyfill for nodejs', () => { polyfillPerformance(); expect(global.performance).toBe(originalPerformance); } - const fakePerformance = (jest.fn() as unknown) as Performance; + const fakePerformance = jest.fn() as unknown as Performance; global.performance = fakePerformance; polyfillPerformance(); expect(global.performance).toEqual(fakePerformance); @@ -72,9 +72,11 @@ describe('polyfill for nodejs', () => { }); it('should not polyfill requestAnimationFrame if it already exists', () => { - const fakeRequestAnimationFrame = (jest.fn() as unknown) as typeof global.requestAnimationFrame; + const fakeRequestAnimationFrame = + jest.fn() as unknown as typeof global.requestAnimationFrame; global.requestAnimationFrame = fakeRequestAnimationFrame; - const fakeCancelAnimationFrame = (jest.fn() as unknown) as typeof global.cancelAnimationFrame; + const fakeCancelAnimationFrame = + jest.fn() as unknown as typeof global.cancelAnimationFrame; global.cancelAnimationFrame = fakeCancelAnimationFrame; polyfillRAF(); expect(global.requestAnimationFrame).toBe(fakeRequestAnimationFrame); @@ -91,7 +93,7 @@ describe('polyfill for nodejs', () => { }); it('should not polyfill Event type if it already exists', () => { - const fakeEvent = (jest.fn() as unknown) as typeof global.Event; + const fakeEvent = jest.fn() as unknown as typeof global.Event; global.Event = fakeEvent; polyfillEvent(); expect(global.Event).toBe(fakeEvent); @@ -106,7 +108,7 @@ describe('polyfill for nodejs', () => { }); it('should not polyfill Node type if it already exists', () => { - const fakeNode = (jest.fn() as unknown) as typeof global.Node; + const fakeNode = jest.fn() as unknown as typeof global.Node; global.Node = fakeNode; polyfillNode(); expect(global.Node).toBe(fakeNode); @@ -121,7 +123,7 @@ describe('polyfill for nodejs', () => { }); it('should not polyfill document object if it already exists', () => { - const fakeDocument = (jest.fn() as unknown) as typeof global.document; + const fakeDocument = jest.fn() as unknown as typeof global.document; global.document = fakeDocument; polyfillDocument(); expect(global.document).toBe(fakeDocument); diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 842bfb8953..e4e06e8db5 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -301,10 +301,9 @@ function diffAfterUpdatingChildren( oldTree.textContent !== (newTree as IRRText | IRRComment | IRRCDATASection).data ) - oldTree.textContent = (newTree as - | IRRText - | IRRComment - | IRRCDATASection).data; + oldTree.textContent = ( + newTree as IRRText | IRRComment | IRRCDATASection + ).data; break; } } diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index 9f76f82123..6af6e8198d 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -195,7 +195,7 @@ export class BaseRRNode implements IRRNode { } export function BaseRRDocumentImpl< - RRNode extends ConstrainedConstructor + RRNode extends ConstrainedConstructor, >(RRNodeClass: RRNode) { return class BaseRRDocument extends RRNodeClass implements IRRDocument { public readonly nodeType: number = NodeType.DOCUMENT_NODE; @@ -401,13 +401,14 @@ export function BaseRRDocumentImpl< } export function BaseRRDocumentTypeImpl< - RRNode extends ConstrainedConstructor + RRNode extends ConstrainedConstructor, >(RRNodeClass: RRNode) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRDocumentType extends RRNodeClass - implements IRRDocumentType { + implements IRRDocumentType + { public readonly nodeType: number = NodeType.DOCUMENT_TYPE_NODE; public readonly RRNodeType = RRNodeType.DocumentType; public readonly nodeName: string; @@ -431,7 +432,7 @@ export function BaseRRDocumentTypeImpl< } export function BaseRRElementImpl< - RRNode extends ConstrainedConstructor + RRNode extends ConstrainedConstructor, >(RRNodeClass: RRNode) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -479,9 +480,9 @@ export function BaseRRElementImpl< } public get style() { - const style = (this.attributes.style - ? parseCSSText(this.attributes.style) - : {}) as CSSStyleDeclaration; + const style = ( + this.attributes.style ? parseCSSText(this.attributes.style) : {} + ) as CSSStyleDeclaration; const hyphenateRE = /\B([A-Z])/g; style.setProperty = ( name: string, @@ -583,7 +584,7 @@ export function BaseRRElementImpl< } export function BaseRRMediaElementImpl< - RRElement extends ConstrainedConstructor + RRElement extends ConstrainedConstructor, >(RRElementClass: RRElement) { return class BaseRRMediaElement extends RRElementClass { public currentTime?: number; @@ -637,7 +638,7 @@ export function BaseRRTextImpl>( } export function BaseRRCommentImpl< - RRNode extends ConstrainedConstructor + RRNode extends ConstrainedConstructor, >(RRNodeClass: RRNode) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -667,13 +668,14 @@ export function BaseRRCommentImpl< } export function BaseRRCDATASectionImpl< - RRNode extends ConstrainedConstructor + RRNode extends ConstrainedConstructor, >(RRNodeClass: RRNode) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return class BaseRRCDATASection extends RRNodeClass - implements IRRCDATASection { + implements IRRCDATASection + { public readonly nodeName: '#cdata-section' = '#cdata-section'; public readonly nodeType: number = NodeType.CDATA_SECTION_NODE; public readonly RRNodeType = RRNodeType.CDATA; diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index 2b90840db2..eda0dd1912 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -193,9 +193,8 @@ interface RRElementTagNameMap { video: RRMediaElement; } -type RRElementType< - K extends keyof HTMLElementTagNameMap -> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; +type RRElementType = + K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement; function getValidTagName(element: HTMLElement): string { // https://github.com/rrweb-io/rrweb-snapshot/issues/56 diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 536641b075..77595adea6 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -65,9 +65,11 @@ function createTree( type TNode = typeof rrDocument extends RRDocument ? RRNode : Node; let root: TNode; - root = (rrDocument - ? rrDocument.createElement(treeNode.tagName) - : document.createElement(treeNode.tagName)) as TNode; + root = ( + rrDocument + ? rrDocument.createElement(treeNode.tagName) + : document.createElement(treeNode.tagName) + ) as TNode; const sn = Object.assign({}, elementSn, { tagName: treeNode.tagName, @@ -75,9 +77,9 @@ function createTree( }); if (rrDocument) { - rrDocument.mirror.add((root as unknown) as RRNode, sn); + rrDocument.mirror.add(root as unknown as RRNode, sn); } else { - mirror.add((root as unknown) as Node, sn); + mirror.add(root as unknown as Node, sn); } if (treeNode.children) @@ -320,8 +322,8 @@ describe('diff algorithm for rrdom', () => { rrNode.attributes = { id: 'node1', class: 'node' }; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).id).toBe('node1'); - expect(((node as Node) as HTMLElement).className).toBe('node'); + expect((node as Node as HTMLElement).id).toBe('node1'); + expect((node as Node as HTMLElement).className).toBe('node'); }); it('can update exist properties', () => { @@ -330,9 +332,9 @@ describe('diff algorithm for rrdom', () => { const sn = Object.assign({}, elementSn, { tagName }); mirror.add(node, sn); - ((node as Node) as HTMLElement).id = 'element1'; - ((node as Node) as HTMLElement).className = 'element'; - ((node as Node) as HTMLElement).setAttribute('style', 'color: black'); + (node as Node as HTMLElement).id = 'element1'; + (node as Node as HTMLElement).className = 'element'; + (node as Node as HTMLElement).setAttribute('style', 'color: black'); const rrDocument = new RRDocument(); const rrNode = rrDocument.createElement(tagName); const sn2 = Object.assign({}, elementSn, { tagName }); @@ -340,17 +342,17 @@ describe('diff algorithm for rrdom', () => { rrNode.attributes = { id: 'node1', class: 'node', style: 'color: white' }; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).id).toBe('node1'); - expect(((node as Node) as HTMLElement).className).toBe('node'); - expect(((node as Node) as HTMLElement).getAttribute('style')).toBe( + expect((node as Node as HTMLElement).id).toBe('node1'); + expect((node as Node as HTMLElement).className).toBe('node'); + expect((node as Node as HTMLElement).getAttribute('style')).toBe( 'color: white', ); rrNode.attributes = { id: 'node2' }; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).id).toBe('node2'); - expect(((node as Node) as HTMLElement).className).toBe(''); - expect(((node as Node) as HTMLElement).getAttribute('style')).toBe(null); + expect((node as Node as HTMLElement).id).toBe('node2'); + expect((node as Node as HTMLElement).className).toBe(''); + expect((node as Node as HTMLElement).getAttribute('style')).toBe(null); }); it('can delete old properties', () => { @@ -359,9 +361,9 @@ describe('diff algorithm for rrdom', () => { const sn = Object.assign({}, elementSn, { tagName }); mirror.add(node, sn); - ((node as Node) as HTMLElement).id = 'element1'; - ((node as Node) as HTMLElement).className = 'element'; - ((node as Node) as HTMLElement).setAttribute('style', 'color: black'); + (node as Node as HTMLElement).id = 'element1'; + (node as Node as HTMLElement).className = 'element'; + (node as Node as HTMLElement).setAttribute('style', 'color: black'); const rrDocument = new RRDocument(); const rrNode = rrDocument.createElement(tagName); const sn2 = Object.assign({}, elementSn, { tagName }); @@ -369,15 +371,15 @@ describe('diff algorithm for rrdom', () => { rrNode.attributes = { id: 'node1' }; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).id).toBe('node1'); - expect(((node as Node) as HTMLElement).className).toBe(''); - expect(((node as Node) as HTMLElement).getAttribute('style')).toBe(null); + expect((node as Node as HTMLElement).id).toBe('node1'); + expect((node as Node as HTMLElement).className).toBe(''); + expect((node as Node as HTMLElement).getAttribute('style')).toBe(null); rrNode.attributes = { src: 'link' }; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).id).toBe(''); - expect(((node as Node) as HTMLElement).className).toBe(''); - expect(((node as Node) as HTMLElement).getAttribute('src')).toBe('link'); + expect((node as Node as HTMLElement).id).toBe(''); + expect((node as Node as HTMLElement).className).toBe(''); + expect((node as Node as HTMLElement).getAttribute('src')).toBe('link'); }); it('can diff scroll positions', () => { @@ -386,8 +388,8 @@ describe('diff algorithm for rrdom', () => { const sn = Object.assign({}, elementSn, { tagName }); mirror.add(node, sn); - expect(((node as Node) as HTMLElement).scrollLeft).toEqual(0); - expect(((node as Node) as HTMLElement).scrollTop).toEqual(0); + expect((node as Node as HTMLElement).scrollLeft).toEqual(0); + expect((node as Node as HTMLElement).scrollTop).toEqual(0); const rrDocument = new RRDocument(); const rrNode = rrDocument.createElement(tagName); const sn2 = Object.assign({}, elementSn, { tagName }); @@ -396,8 +398,8 @@ describe('diff algorithm for rrdom', () => { rrNode.scrollLeft = 100; rrNode.scrollTop = 200; diff(node, rrNode, replayer); - expect(((node as Node) as HTMLElement).scrollLeft).toEqual(100); - expect(((node as Node) as HTMLElement).scrollTop).toEqual(200); + expect((node as Node as HTMLElement).scrollLeft).toEqual(100); + expect((node as Node as HTMLElement).scrollTop).toEqual(200); }); it('can diff properties for SVG elements', () => { @@ -412,9 +414,7 @@ describe('diff algorithm for rrdom', () => { jest.spyOn(Element.prototype, 'setAttributeNS'); diff(element, node, replayer); - expect(((element as Node) as SVGElement).getAttribute('xmlns')).toBe( - value, - ); + expect((element as Node as SVGElement).getAttribute('xmlns')).toBe(value); expect(SVGElement.prototype.setAttributeNS).toHaveBeenCalledWith( 'http://www.w3.org/2000/xmlns/', 'xmlns', @@ -464,9 +464,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(3); expect(rrNode.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, + 1, 2, 3, ]); }); @@ -494,11 +492,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(5); expect(rrNode.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); }); @@ -526,11 +520,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(5); expect(rrNode.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); }); @@ -558,11 +548,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(5); expect(rrNode.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); }); @@ -589,9 +575,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(3); expect(rrNode.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, + 1, 2, 3, ]); }); @@ -631,11 +615,7 @@ describe('diff algorithm for rrdom', () => { ) as Node; expect(node.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); const rrNode = createTree( { @@ -650,9 +630,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(3); expect(rrNode.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 3, - 4, - 5, + 3, 4, 5, ]); }); @@ -668,11 +646,7 @@ describe('diff algorithm for rrdom', () => { ) as Node; expect(node.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); const rrNode = createTree( { @@ -687,9 +661,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(3); expect(rrNode.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, + 1, 2, 3, ]); }); @@ -705,11 +677,7 @@ describe('diff algorithm for rrdom', () => { ) as Node; expect(node.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); const rrNode = createTree( { @@ -724,10 +692,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(4); expect(rrNode.childNodes.length).toEqual(4); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 4, - 5, + 1, 2, 4, 5, ]); }); @@ -743,11 +708,7 @@ describe('diff algorithm for rrdom', () => { ) as Node; expect(node.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, - 4, - 5, + 1, 2, 3, 4, 5, ]); const rrNode = createTree( { @@ -762,11 +723,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(5); expect(rrNode.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 2, - 3, - 4, - 1, - 5, + 2, 3, 4, 1, 5, ]); }); @@ -782,9 +739,7 @@ describe('diff algorithm for rrdom', () => { ) as Node; expect(node.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 2, - 3, + 1, 2, 3, ]); const rrNode = createTree( { @@ -799,9 +754,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(3); expect(rrNode.childNodes.length).toEqual(3); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 2, - 3, - 1, + 2, 3, 1, ]); }); @@ -829,10 +782,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(4); expect(rrNode.childNodes.length).toEqual(4); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 1, - 4, - 2, - 3, + 1, 4, 2, 3, ]); }); @@ -860,10 +810,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(4); expect(rrNode.childNodes.length).toEqual(4); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 4, - 2, - 3, - 1, + 4, 2, 3, 1, ]); }); @@ -891,11 +838,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(5); expect(rrNode.childNodes.length).toEqual(5); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 4, - 1, - 2, - 3, - 6, + 4, 1, 2, 3, 6, ]); }); @@ -923,8 +866,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(2); expect(rrNode.childNodes.length).toEqual(2); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 4, - 6, + 4, 6, ]); }); @@ -958,14 +900,7 @@ describe('diff algorithm for rrdom', () => { expect(node.childNodes.length).toEqual(8); expect(rrNode.childNodes.length).toEqual(8); expect(Array.from(node.childNodes).map((c) => mirror.getId(c))).toEqual([ - 8, - 7, - 6, - 5, - 4, - 3, - 2, - 1, + 8, 7, 6, 5, 4, 3, 2, 1, ]); }); @@ -1131,7 +1066,7 @@ describe('diff algorithm for rrdom', () => { id: 1, } as serializedNodeWithId); - expect(((node as Node) as HTMLElement).shadowRoot).toBeNull(); + expect((node as Node as HTMLElement).shadowRoot).toBeNull(); const rrDocument = new RRDocument(); const rrNode = rrDocument.createElement(tagName); @@ -1147,11 +1082,11 @@ describe('diff algorithm for rrdom', () => { expect(rrNode.shadowRoot!.childNodes.length).toBe(1); diff(node, rrNode, replayer, rrDocument.mirror); - expect(((node as Node) as HTMLElement).shadowRoot).not.toBeNull(); - expect( - ((node as Node) as HTMLElement).shadowRoot!.childNodes.length, - ).toBe(1); - const childElement = ((node as Node) as HTMLElement).shadowRoot! + expect((node as Node as HTMLElement).shadowRoot).not.toBeNull(); + expect((node as Node as HTMLElement).shadowRoot!.childNodes.length).toBe( + 1, + ); + const childElement = (node as Node as HTMLElement).shadowRoot! .childNodes[0] as HTMLElement; expect(childElement.tagName).toEqual('DIV'); }); @@ -1623,7 +1558,7 @@ describe('diff algorithm for rrdom', () => { let result = createOrGetNode(rrNode, mirror, rrDocument.mirror); expect(result).toBeInstanceOf(HTMLElement); expect(mirror.getId(result)).toBe(0); - expect(((result as Node) as HTMLElement).tagName).toBe('DIV'); + expect((result as Node as HTMLElement).tagName).toBe('DIV'); }); it('create a node from RRNode', () => { @@ -1639,14 +1574,14 @@ describe('diff algorithm for rrdom', () => { result = createOrGetNode(rrNode, mirror, rrDocument.mirror); expect(result).toBeInstanceOf(Text); expect(mirror.getId(result)).toBe(1); - expect(((result as Node) as Text).textContent).toBe(textContent); + expect((result as Node as Text).textContent).toBe(textContent); rrNode = rrDocument.createComment(textContent); rrDocument.mirror.add(rrNode, getDefaultSN(rrNode, 2)); result = createOrGetNode(rrNode, mirror, rrDocument.mirror); expect(result).toBeInstanceOf(Comment); expect(mirror.getId(result)).toBe(2); - expect(((result as Node) as Comment).textContent).toBe(textContent); + expect((result as Node as Comment).textContent).toBe(textContent); rrNode = rrDocument.createCDATASection(''); rrDocument.mirror.add(rrNode, getDefaultSN(rrNode, 3)); @@ -1665,9 +1600,9 @@ describe('diff algorithm for rrdom', () => { let result = createOrGetNode(rrNode, mirror, rrDocument.mirror); expect(result).toBeInstanceOf(DocumentType); expect(mirror.getId(result)).toBe(0); - expect(((result as Node) as DocumentType).name).toEqual('html'); - expect(((result as Node) as DocumentType).publicId).toEqual(publicId); - expect(((result as Node) as DocumentType).systemId).toEqual(''); + expect((result as Node as DocumentType).name).toEqual('html'); + expect((result as Node as DocumentType).publicId).toEqual(publicId); + expect((result as Node as DocumentType).systemId).toEqual(''); }); it('can get a node if it already exists', () => { @@ -1687,7 +1622,7 @@ describe('diff algorithm for rrdom', () => { expect(result).toBeInstanceOf(Text); expect(mirror.getId(result)).toBe(0); - expect(((result as Node) as Text).textContent).toBe(textContent); + expect((result as Node as Text).textContent).toBe(textContent); expect(result).toEqual(text); // To make sure the existed text node is used. expect(mirror.getMeta(result)).toEqual(mirror.getMeta(text)); @@ -1696,7 +1631,7 @@ describe('diff algorithm for rrdom', () => { describe('apply virtual style rules to node', () => { beforeEach(() => { - const dummyReplayer = new Replayer(([ + const dummyReplayer = new Replayer([ { type: EventType.DomContentLoaded, timestamp: 0, @@ -1709,7 +1644,7 @@ describe('diff algorithm for rrdom', () => { }, timestamp: 0, }, - ] as unknown) as eventWithTime[]); + ] as unknown as eventWithTime[]); replayer.applyStyleSheetMutation = ( data: styleDeclarationData | styleSheetRuleData, styleSheet: CSSStyleSheet, diff --git a/packages/rrdom/test/document.test.ts b/packages/rrdom/test/document.test.ts index 47d9fa73e8..505cc700e4 100644 --- a/packages/rrdom/test/document.test.ts +++ b/packages/rrdom/test/document.test.ts @@ -424,7 +424,7 @@ describe('Basic RRDocument implementation', () => { expect(node.removeAttribute).toBeDefined(); expect(node.attachShadow).toBeDefined(); expect(node.dispatchEvent).toBeDefined(); - expect(node.dispatchEvent((null as unknown) as Event)).toBeTruthy(); + expect(node.dispatchEvent(null as unknown as Event)).toBeTruthy(); expect(node.toString()).toEqual('DIV id="id" class="className" '); }); diff --git a/packages/rrdom/test/utils.ts b/packages/rrdom/test/utils.ts index a189ae365a..6fe53920d2 100644 --- a/packages/rrdom/test/utils.ts +++ b/packages/rrdom/test/utils.ts @@ -1,7 +1,7 @@ import * as rollup from 'rollup'; import * as typescript from 'rollup-plugin-typescript2'; import resolve from '@rollup/plugin-node-resolve'; -const _typescript = (typescript as unknown) as typeof typescript.default; +const _typescript = typescript as unknown as typeof typescript.default; /** * Use rollup to compile an input TS script into JS code string. @@ -10,10 +10,10 @@ export async function compileTSCode(inputFilePath: string) { const bundle = await rollup.rollup({ input: inputFilePath, plugins: [ - (resolve() as unknown) as rollup.Plugin, - (_typescript({ + resolve() as unknown as rollup.Plugin, + _typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, - }) as unknown) as rollup.Plugin, + }) as unknown as rollup.Plugin, ], }); const { diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index 0b25f684b2..b99aca5ae4 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -98,7 +98,7 @@ describe('RRDocument for browser environment', () => { // build from element expect(mirror.getMeta(document.documentElement)).toBeNull(); rrNode = buildFromNode( - (document.documentElement as unknown) as Node, + document.documentElement as unknown as Node, rrdom, mirror, )!; @@ -378,7 +378,7 @@ describe('RRDocument for browser environment', () => { expect(dom.mirror.getId(node1)).toEqual(0); const node2 = dom.createTextNode('text'); expect(dom.mirror.getId(node2)).toEqual(-1); - expect(dom.mirror.getId((null as unknown) as RRNode)).toEqual(-1); + expect(dom.mirror.getId(null as unknown as RRNode)).toEqual(-1); }); it('has() should return whether the mirror has an ID', () => { diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index a491122d6b..bb9e61d645 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -267,8 +267,8 @@ function buildNode( rr_dataURL: string; }; // If the canvas element is created in RRDom runtime (seeking to a time point), the canvas context isn't supported. So the data has to be stored and not handled until diff process. https://github.com/rrweb-io/rrweb/pull/944 - if (((node as unknown) as RRCanvasElement).RRNodeType) - ((node as unknown) as RRCanvasElement).rr_dataURL = value.toString(); + if ((node as unknown as RRCanvasElement).RRNodeType) + (node as unknown as RRCanvasElement).rr_dataURL = value.toString(); } else if (tagName === 'img' && name === 'rr_dataURL') { const image = node as HTMLImageElement; if (!image.currentSrc.startsWith('data:')) { diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 75e9a18008..d505ad0353 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -110,7 +110,7 @@ export class Mirror implements IMirror { if (n.childNodes) { n.childNodes.forEach((childNode) => - this.removeNodeFromMap((childNode as unknown) as Node), + this.removeNodeFromMap(childNode as unknown as Node), ); } } diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index ab31b58c4a..c937726071 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -8,7 +8,7 @@ import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; import { waitForRAF } from './utils'; -const _typescript = (typescript as unknown) as () => rollup.Plugin; +const _typescript = typescript as unknown as () => rollup.Plugin; const htmlFolder = path.join(__dirname, 'html'); const htmls = fs.readdirSync(htmlFolder).map((filePath) => { @@ -129,7 +129,8 @@ describe('integration tests', function (this: ISuite) { }); } await waitForRAF(page); - const rebuildHtml = ((await page.evaluate(`${code} + const rebuildHtml = ( + (await page.evaluate(`${code} const x = new XMLSerializer(); const snap = rrweb.snapshot(document); let out = x.serializeToString(rrweb.rebuild(snap, { doc: document })); @@ -138,7 +139,8 @@ describe('integration tests', function (this: ISuite) { out = out.replace(' xmlns=\"http://www.w3.org/1999/xhtml\"', ''); } out; // return - `)) as string) + `)) as string + ) .replace(/\n\n/g, '') .replace( /blob:http:\/\/localhost:\d+\/[0-9a-z\-]+/, diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 221292c486..3413cc0727 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -90,9 +90,9 @@ function initLogObserver( win: IWindow, // top window or in an iframe options: LogRecordOptions, ): listenerHandler { - const logOptions = (options - ? Object.assign({}, defaultLogOptions, options) - : defaultLogOptions) as { + const logOptions = ( + options ? Object.assign({}, defaultLogOptions, options) : defaultLogOptions + ) as { level: LogLevel[]; lengthThreshold: number; stringifyOptions?: StringifyOptions; @@ -117,9 +117,9 @@ function initLogObserver( const errorHandler = (event: ErrorEvent) => { const message = event.message, error = event.error as Error; - const trace: string[] = ErrorStackParser.parse( - error, - ).map((stackFrame: StackFrame) => stackFrame.toString()); + const trace: string[] = ErrorStackParser.parse(error).map( + (stackFrame: StackFrame) => stackFrame.toString(), + ); const payload = [stringify(message, logOptions.stringifyOptions)]; cb({ level: 'error', @@ -149,9 +149,9 @@ function initLogObserver( stringify(event.reason, logOptions.stringifyOptions), ]; } - const trace: string[] = ErrorStackParser.parse( - error, - ).map((stackFrame: StackFrame) => stackFrame.toString()); + const trace: string[] = ErrorStackParser.parse(error).map( + (stackFrame: StackFrame) => stackFrame.toString(), + ); cb({ level: 'error', trace, diff --git a/packages/rrweb/src/plugins/console/record/stringify.ts b/packages/rrweb/src/plugins/console/record/stringify.ts index eef8c38a40..a767793e8f 100644 --- a/packages/rrweb/src/plugins/console/record/stringify.ts +++ b/packages/rrweb/src/plugins/console/record/stringify.ts @@ -123,7 +123,7 @@ export function stringify( if (value instanceof Event) { const eventResult: Record = {}; for (const eventKey in value) { - const eventValue = ((value as unknown) as Record)[ + const eventValue = (value as unknown as Record)[ eventKey ]; if (Array.isArray(eventValue)) { diff --git a/packages/rrweb/src/plugins/console/replay/index.ts b/packages/rrweb/src/plugins/console/replay/index.ts index db56e18c70..a0915fa6af 100644 --- a/packages/rrweb/src/plugins/console/replay/index.ts +++ b/packages/rrweb/src/plugins/console/replay/index.ts @@ -59,10 +59,10 @@ class LogReplayPlugin { for (const level of this.config.level!) { if (level === 'trace') { replayLogger[level] = (data: LogData) => { - const logger = ((console.log as unknown) as PatchedConsoleLog)[ + const logger = (console.log as unknown as PatchedConsoleLog)[ ORIGINAL_ATTRIBUTE_NAME ] - ? ((console.log as unknown) as PatchedConsoleLog)[ + ? (console.log as unknown as PatchedConsoleLog)[ ORIGINAL_ATTRIBUTE_NAME ] : console.log; @@ -73,10 +73,10 @@ class LogReplayPlugin { }; } else { replayLogger[level] = (data: LogData) => { - const logger = ((console[level] as unknown) as PatchedConsoleLog)[ + const logger = (console[level] as unknown as PatchedConsoleLog)[ ORIGINAL_ATTRIBUTE_NAME ] - ? ((console[level] as unknown) as PatchedConsoleLog)[ + ? (console[level] as unknown as PatchedConsoleLog)[ ORIGINAL_ATTRIBUTE_NAME ] : console[level]; @@ -118,7 +118,7 @@ export const getReplayConsolePlugin: ( event.type === EventType.IncrementalSnapshot && event.data.source === (IncrementalSource.Log as IncrementalSource) ) { - logData = (event.data as unknown) as LogData; + logData = event.data as unknown as LogData; } else if ( event.type === EventType.Plugin && event.data.plugin === PLUGIN_NAME diff --git a/packages/rrweb/src/plugins/sequential-id/replay/index.ts b/packages/rrweb/src/plugins/sequential-id/replay/index.ts index a41edb8474..0f3d94b820 100644 --- a/packages/rrweb/src/plugins/sequential-id/replay/index.ts +++ b/packages/rrweb/src/plugins/sequential-id/replay/index.ts @@ -22,7 +22,7 @@ export const getReplaySequentialIdPlugin: ( return { handler(event: eventWithTime) { if (key in event) { - const id = ((event as unknown) as Record)[key]; + const id = (event as unknown as Record)[key]; if (id !== currentId) { console.error( `[sequential-id-plugin]: expect to get an id with value "${currentId}", but got "${id}"`, diff --git a/packages/rrweb/src/record/cross-origin-iframe-mirror.ts b/packages/rrweb/src/record/cross-origin-iframe-mirror.ts index cec738084b..4e7a69326e 100644 --- a/packages/rrweb/src/record/cross-origin-iframe-mirror.ts +++ b/packages/rrweb/src/record/cross-origin-iframe-mirror.ts @@ -1,6 +1,7 @@ import type { ICrossOriginIframeMirror } from '@rrweb/types'; export default class CrossOriginIframeMirror - implements ICrossOriginIframeMirror { + implements ICrossOriginIframeMirror +{ private iframeIdToRemoteIdMap: WeakMap< HTMLIFrameElement, Map diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index d62713ff0d..377b7bc0ff 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -8,16 +8,12 @@ import type { StylesheetManager } from './stylesheet-manager'; export class IframeManager { private iframes: WeakMap = new WeakMap(); - private crossOriginIframeMap: WeakMap< - MessageEventSource, - HTMLIFrameElement - > = new WeakMap(); + private crossOriginIframeMap: WeakMap = + new WeakMap(); public crossOriginIframeMirror = new CrossOriginIframeMirror(genId); public crossOriginIframeStyleMirror: CrossOriginIframeMirror; - public crossOriginIframeRootIdMap: WeakMap< - HTMLIFrameElement, - number - > = new WeakMap(); + public crossOriginIframeRootIdMap: WeakMap = + new WeakMap(); private mirror: Mirror; private mutationCb: mutationCallBack; private wrappedEmit: (e: eventWithTime, isCheckout?: boolean) => void; diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 6c79007690..e7bea4f204 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -173,9 +173,9 @@ function record( // Disable packing events which will be emitted to parent frames. !passEmitsToParent ) { - e = (packFn(e) as unknown) as eventWithTime; + e = packFn(e) as unknown as eventWithTime; } - return (e as unknown) as T; + return e as unknown as T; }; wrappedEmit = (e: eventWithTime, isCheckout?: boolean) => { if ( diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index d9e27496a2..5c3e4bea7d 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -180,29 +180,31 @@ export default class MutationBuffer { private processedNodeManager: observerParam['processedNodeManager']; public init(options: MutationBufferParam) { - ([ - 'mutationCb', - 'blockClass', - 'blockSelector', - 'maskTextClass', - 'maskTextSelector', - 'inlineStylesheet', - 'maskInputOptions', - 'maskTextFn', - 'maskInputFn', - 'keepIframeSrcFn', - 'recordCanvas', - 'inlineImages', - 'slimDOMOptions', - 'dataURLOptions', - 'doc', - 'mirror', - 'iframeManager', - 'stylesheetManager', - 'shadowDomManager', - 'canvasManager', - 'processedNodeManager', - ] as const).forEach((key) => { + ( + [ + 'mutationCb', + 'blockClass', + 'blockSelector', + 'maskTextClass', + 'maskTextSelector', + 'inlineStylesheet', + 'maskInputOptions', + 'maskTextFn', + 'maskInputFn', + 'keepIframeSrcFn', + 'recordCanvas', + 'inlineImages', + 'slimDOMOptions', + 'dataURLOptions', + 'doc', + 'mirror', + 'iframeManager', + 'stylesheetManager', + 'shadowDomManager', + 'canvasManager', + 'processedNodeManager', + ] as const + ).forEach((key) => { // just a type trick, the runtime result is correct this[key] = options[key] as never; }); diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index e7284104b9..76a78c6fec 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -94,19 +94,18 @@ export function initMutationObserver( * window.__rrMutationObserver = MutationObserver */ (window as WindowWithStoredMutationObserver).__rrMutationObserver; - const angularZoneSymbol = (window as WindowWithAngularZone)?.Zone?.__symbol__?.( - 'MutationObserver', - ); + const angularZoneSymbol = ( + window as WindowWithAngularZone + )?.Zone?.__symbol__?.('MutationObserver'); if ( angularZoneSymbol && - ((window as unknown) as Record)[ + (window as unknown as Record)[ angularZoneSymbol ] ) { - mutationObserverCtor = ((window as unknown) as Record< - string, - typeof MutationObserver - >)[angularZoneSymbol]; + mutationObserverCtor = ( + window as unknown as Record + )[angularZoneSymbol]; } const observer = new (mutationObserverCtor as new ( callback: MutationCallback, @@ -423,9 +422,9 @@ function initInputObserver({ } } const events = sampling.input === 'last' ? ['change'] : ['input', 'change']; - const handlers: Array< - listenerHandler | hookResetter - > = events.map((eventName) => on(eventName, eventHandler, doc)); + const handlers: Array = events.map( + (eventName) => on(eventName, eventHandler, doc), + ); const currentWindow = doc.defaultView; if (!currentWindow) { return () => { @@ -891,12 +890,8 @@ function initMediaInteractionObserver({ ) { return; } - const { - currentTime, - volume, - muted, - playbackRate, - } = target as HTMLMediaElement; + const { currentTime, volume, muted, playbackRate } = + target as HTMLMediaElement; mediaInteractionCb({ type, id: mirror.getId(target as Node), @@ -931,7 +926,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { const fontMap = new WeakMap(); const originalFontFace = win.FontFace; - win.FontFace = (function FontFace( + win.FontFace = function FontFace( family: string, source: string | ArrayBufferLike, descriptors?: FontFaceDescriptors, @@ -947,7 +942,7 @@ function initFontObserver({ fontCb, doc }: observerParam): listenerHandler { : JSON.stringify(Array.from(new Uint8Array(source))), }); return fontFace; - } as unknown) as typeof FontFace; + } as unknown as typeof FontFace; const restoreHandler = patch( doc.fonts, diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index a27bac8d2a..ad6781dd58 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -116,7 +116,8 @@ export class CanvasManager { blockSelector, ); const snapshotInProgressMap: Map = new Map(); - const worker = new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker; + const worker = + new ImageBitmapDataURLWorker() as ImageBitmapDataURLRequestWorker; worker.onmessage = (e) => { const { id } = e.data; snapshotInProgressMap.set(id, false); diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index fd90d23d90..22034db17b 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -81,7 +81,7 @@ export class ShadowDomManager { scrollCb: this.scrollCb, // https://gist.github.com/praveenpuglia/0832da687ed5a5d7a0907046c9ef1813 // scroll is not allowed to pass the boundary, so we need to listen the shadow document - doc: (shadowRoot as unknown) as Document, + doc: shadowRoot as unknown as Document, mirror: this.mirror, }); // Defer this to avoid adoptedStyleSheet events being created before the full snapshot is created or attachShadow action is recorded. @@ -113,9 +113,11 @@ export class ShadowDomManager { const manager = this; this.restorePatches.push( patch( - (iframeElement.contentWindow as Window & { - HTMLElement: { prototype: HTMLElement }; - }).HTMLElement.prototype, + ( + iframeElement.contentWindow as Window & { + HTMLElement: { prototype: HTMLElement }; + } + ).HTMLElement.prototype, 'attachShadow', function (original: (init: ShadowRootInit) => ShadowRoot) { return function (this: HTMLElement, option: ShadowRootInit) { diff --git a/packages/rrweb/src/replay/canvas/2d.ts b/packages/rrweb/src/replay/canvas/2d.ts index ac6fdf4119..f9fefba226 100644 --- a/packages/rrweb/src/replay/canvas/2d.ts +++ b/packages/rrweb/src/replay/canvas/2d.ts @@ -20,7 +20,7 @@ export default async function canvasMutation({ if (mutation.setter) { // skip some read-only type checks - ((ctx as unknown) as Record)[mutation.property] = + (ctx as unknown as Record)[mutation.property] = mutation.args[0]; return; } diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 1b1f6b44fa..396efe76f5 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -434,9 +434,10 @@ export class Replayer { public getMetaData(): playerMetaData { const firstEvent = this.service.state.context.events[0]; - const lastEvent = this.service.state.context.events[ - this.service.state.context.events.length - 1 - ]; + const lastEvent = + this.service.state.context.events[ + this.service.state.context.events.length - 1 + ]; return { startTime: firstEvent.timestamp, endTime: lastEvent.timestamp, @@ -855,7 +856,7 @@ export class Replayer { const collected: AppendedIframe[] = []; const afterAppend = (builtNode: Node, id: number) => { this.collectIframeAndAttachDocument(collected, builtNode); - const sn = (mirror as TMirror).getMeta((builtNode as unknown) as TNode); + const sn = (mirror as TMirror).getMeta(builtNode as unknown as TNode); if ( sn?.type === NodeType.Element && sn?.tagName.toUpperCase() === 'HTML' @@ -1263,9 +1264,9 @@ export class Replayer { if (this.usingVirtualDom) { if (d.styleId) this.constructedStyleMutations.push(d); else if (d.id) - (this.virtualDom.mirror.getNode( - d.id, - ) as RRStyleElement | null)?.rules.push(d); + ( + this.virtualDom.mirror.getNode(d.id) as RRStyleElement | null + )?.rules.push(d); } else this.applyStyleSheetMutation(d); break; } @@ -1916,10 +1917,10 @@ export class Replayer { styleSheet: CSSStyleSheet, ) { if (data.set) { - const rule = (getNestedRule( + const rule = getNestedRule( styleSheet.rules, data.index, - ) as unknown) as CSSStyleRule; + ) as unknown as CSSStyleRule; rule.style.setProperty( data.set.property, data.set.value, @@ -1928,10 +1929,10 @@ export class Replayer { } if (data.remove) { - const rule = (getNestedRule( + const rule = getNestedRule( styleSheet.rules, data.index, - ) as unknown) as CSSStyleRule; + ) as unknown as CSSStyleRule; rule.style.removeProperty(data.remove.property); } } @@ -1977,7 +1978,8 @@ export class Replayer { .filter((style) => style !== null) as CSSStyleSheet[]; if (hasShadowRoot(targetHost)) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (targetHost as HTMLElement).shadowRoot!.adoptedStyleSheets = stylesToAdopt; + (targetHost as HTMLElement).shadowRoot!.adoptedStyleSheets = + stylesToAdopt; else if (targetHost.nodeName === '#document') (targetHost as Document).adoptedStyleSheets = stylesToAdopt; diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index c1f5f0d97a..ad35af0039 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -209,4 +209,5 @@ export type CrossOriginIframeMessageEventContent = { origin: string; isCheckout?: boolean; }; -export type CrossOriginIframeMessageEvent = MessageEvent; +export type CrossOriginIframeMessageEvent = + MessageEvent; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 8703191f6d..578160273b 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -281,14 +281,14 @@ export function isTouchEvent( export function polyfill(win = window) { if ('NodeList' in win && !win.NodeList.prototype.forEach) { // eslint-disable-next-line @typescript-eslint/unbound-method - win.NodeList.prototype.forEach = (Array.prototype - .forEach as unknown) as NodeList['forEach']; + win.NodeList.prototype.forEach = Array.prototype + .forEach as unknown as NodeList['forEach']; } if ('DOMTokenList' in win && !win.DOMTokenList.prototype.forEach) { // eslint-disable-next-line @typescript-eslint/unbound-method - win.DOMTokenList.prototype.forEach = (Array.prototype - .forEach as unknown) as DOMTokenList['forEach']; + win.DOMTokenList.prototype.forEach = Array.prototype + .forEach as unknown as DOMTokenList['forEach']; } // https://github.com/Financial-Times/polyfill-service/pull/183 @@ -433,7 +433,7 @@ export function getBaseDimension( export function hasShadowRoot( n: T, ): n is T & { shadowRoot: ShadowRoot } { - return Boolean(((n as unknown) as Element)?.shadowRoot); + return Boolean((n as unknown as Element)?.shadowRoot); } export function getNestedRule( diff --git a/packages/rrweb/test/events/style-sheet-rule-events.ts b/packages/rrweb/test/events/style-sheet-rule-events.ts index b3d50a9990..577c0caf41 100644 --- a/packages/rrweb/test/events/style-sheet-rule-events.ts +++ b/packages/rrweb/test/events/style-sheet-rule-events.ts @@ -147,8 +147,7 @@ const events: eventWithTime[] = [ id: 101, adds: [ { - rule: - '.css-added-at-500-overwritten-at-3000 {border: 1px solid blue;}', + rule: '.css-added-at-500-overwritten-at-3000 {border: 1px solid blue;}', index: 1, }, ], @@ -163,8 +162,7 @@ const events: eventWithTime[] = [ id: 105, adds: [ { - rule: - '.css-added-at-1000-deleted-at-2500{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-width:60rem;min-height:100vh;color:blue;}', + rule: '.css-added-at-1000-deleted-at-2500{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;min-width:60rem;min-height:100vh;color:blue;}', index: 2, }, ], diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 0371914fe6..fed6aae61a 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -502,7 +502,8 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about:blank'); await page.setContent( getHtml('log.html', { - plugins: ('[rrwebConsoleRecord.getRecordConsolePlugin()]' as unknown) as RecordPlugin[], + plugins: + '[rrwebConsoleRecord.getRecordConsolePlugin()]' as unknown as RecordPlugin[], }), ); @@ -790,8 +791,8 @@ describe('record integration tests', function (this: ISuite) { await page.evaluate(() => { // get contentDocument of iframe five - const contentDocument1 = document.querySelector('iframe')! - .contentDocument!; + const contentDocument1 = + document.querySelector('iframe')!.contentDocument!; // create shadow dom #1 contentDocument1.body.attachShadow({ mode: 'open' }); contentDocument1.body.shadowRoot!.appendChild( diff --git a/packages/rrweb/test/packer.test.ts b/packages/rrweb/test/packer.test.ts index ed2fbadf25..e6487d7a20 100644 --- a/packages/rrweb/test/packer.test.ts +++ b/packages/rrweb/test/packer.test.ts @@ -17,7 +17,7 @@ describe('pack', () => { describe('unpack', () => { it('is compatible with unpacked data 1', () => { - const result = unpack((event as unknown) as string); + const result = unpack(event as unknown as string); expect(result).toEqual(event); }); diff --git a/packages/rrweb/test/record.test.ts b/packages/rrweb/test/record.test.ts index 46ad5e5c7e..1a0a87421f 100644 --- a/packages/rrweb/test/record.test.ts +++ b/packages/rrweb/test/record.test.ts @@ -96,9 +96,9 @@ describe('record', function (this: ISuite) { it('will only have one full snapshot without checkout config', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }); let count = 30; @@ -120,9 +120,9 @@ describe('record', function (this: ISuite) { it('can checkout full snapshot by count', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, checkoutEveryNth: 10, }); }); @@ -149,9 +149,9 @@ describe('record', function (this: ISuite) { it('can checkout full snapshot by time', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, checkoutEveryNms: 500, }); }); @@ -182,9 +182,9 @@ describe('record', function (this: ISuite) { it('is safe to checkout during async callbacks', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, checkoutEveryNth: 2, }); const p = document.createElement('p'); @@ -208,9 +208,9 @@ describe('record', function (this: ISuite) { it('should record scroll position', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const p = document.createElement('p'); p.innerText = 'testtesttesttesttesttesttesttesttesttest'; @@ -225,9 +225,9 @@ describe('record', function (this: ISuite) { it('should record selection event', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const startNode = document.createElement('p'); @@ -266,9 +266,9 @@ describe('record', function (this: ISuite) { it('can add custom event', async () => { await ctx.page.evaluate(() => { - const { record, addCustomEvent } = ((window as unknown) as IWindow).rrweb; + const { record, addCustomEvent } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); addCustomEvent('tag1', 1); addCustomEvent<{ a: string }>('tag2', { @@ -281,10 +281,10 @@ describe('record', function (this: ISuite) { it('captures stylesheet rules', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const styleElement = document.createElement('style'); @@ -331,10 +331,10 @@ describe('record', function (this: ISuite) { const captureNestedStylesheetRulesTest = async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const styleElement = document.createElement('style'); @@ -392,10 +392,10 @@ describe('record', function (this: ISuite) { it('captures style property changes', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, ignoreCSSAttributes: new Set(['color']), }); @@ -428,7 +428,7 @@ describe('record', function (this: ISuite) { it('captures inserted style text nodes correctly', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; const styleEl = document.createElement(`style`); styleEl.append(document.createTextNode('div { color: red; }')); @@ -436,7 +436,7 @@ describe('record', function (this: ISuite) { document.head.appendChild(styleEl); record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); styleEl.append(document.createTextNode('span { color: orange; }')); @@ -462,11 +462,11 @@ describe('record', function (this: ISuite) { }); await waitForRAF(ctx.page); await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }); await waitForRAF(ctx.page); @@ -487,14 +487,15 @@ describe('record', function (this: ISuite) { document.adoptedStyleSheets = [sheet]; const iframe = document.querySelector('iframe'); - const sheet2 = new (iframe!.contentWindow! as Window & - typeof globalThis).CSSStyleSheet(); + const sheet2 = new ( + iframe!.contentWindow! as Window & typeof globalThis + ).CSSStyleSheet(); // Add stylesheet to an IFrame document. iframe!.contentDocument!.adoptedStyleSheets = [sheet2]; iframe!.contentDocument!.body.innerHTML = '

h1 in iframe

'; - const { rrweb, emit } = (window as unknown) as IWindow; + const { rrweb, emit } = window as unknown as IWindow; rrweb.record({ emit, }); @@ -568,7 +569,7 @@ describe('record', function (this: ISuite) { sheet2.replaceSync!('div {font-size: large;}'); shadowHost.shadowRoot!.adoptedStyleSheets = [sheet2]; - const { rrweb, emit } = (window as unknown) as IWindow; + const { rrweb, emit } = window as unknown as IWindow; rrweb.record({ emit, }); @@ -602,7 +603,7 @@ describe('record', function (this: ISuite) { sheet.replaceSync!('h1 {color: blue;}'); shadowHost.shadowRoot!.adoptedStyleSheets = [sheet]; - const { rrweb, emit } = (window as unknown) as IWindow; + const { rrweb, emit } = window as unknown as IWindow; rrweb.record({ emit, }); @@ -639,11 +640,11 @@ describe('record', function (this: ISuite) { }); await waitForRAF(ctx.page); await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }); await waitForRAF(ctx.page); @@ -683,11 +684,11 @@ describe('record', function (this: ISuite) { it('captures stylesheets that are still loading', async () => { ctx.page.evaluate((serverURL) => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const link1 = document.createElement('link'); @@ -708,11 +709,11 @@ describe('record', function (this: ISuite) { iframe.setAttribute('src', `/html/hello-world.html?2`); document.body.appendChild(iframe); - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }); @@ -743,11 +744,11 @@ describe('record', function (this: ISuite) { // do not `await` the following function, otherwise `waitForResponse` _might_ not be called void ctx.page.evaluate((corsStylesheetURL) => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ inlineStylesheet: true, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); const link1 = document.createElement('link'); @@ -793,8 +794,9 @@ describe('record', function (this: ISuite) { // Add stylesheet to an IFrame document. const iframe = document.querySelector('iframe'); - const sheet3 = new (iframe!.contentWindow! as IWindow & - typeof globalThis).CSSStyleSheet(); + const sheet3 = new ( + iframe!.contentWindow! as IWindow & typeof globalThis + ).CSSStyleSheet(); sheet3.replaceSync!('h1 { color: blue; }'); iframe!.contentDocument!.adoptedStyleSheets = [sheet3]; @@ -803,8 +805,8 @@ describe('record', function (this: ISuite) { ele.innerText = 'h1 in iframe'; iframe!.contentDocument!.body.appendChild(ele); - ((window as unknown) as IWindow).rrweb.record({ - emit: ((window.top as unknown) as IWindow).emit, + (window as unknown as IWindow).rrweb.record({ + emit: (window.top as unknown as IWindow).emit, }); // Make incremental changes to shadow dom. @@ -819,8 +821,9 @@ describe('record', function (this: ISuite) { document.adoptedStyleSheets = [sheet4, sheet, sheet2]; - const sheet5 = new (iframe!.contentWindow! as IWindow & - typeof globalThis).CSSStyleSheet(); + const sheet5 = new ( + iframe!.contentWindow! as IWindow & typeof globalThis + ).CSSStyleSheet(); sheet5.replaceSync!('h2 { color: purple; }'); iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3]; }, 10); @@ -853,9 +856,9 @@ describe('record iframes', function (this: ISuite) { it('captures iframe content in correct order', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }); await waitForRAF(ctx.page); @@ -877,10 +880,10 @@ describe('record iframes', function (this: ISuite) { it('captures stylesheet mutations in iframes', async () => { await ctx.page.evaluate(() => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ // need to reference window.top for when we are in an iframe! - emit: ((window.top as unknown) as IWindow).emit, + emit: (window.top as unknown as IWindow).emit, }); const iframe = document.querySelector('iframe'); diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index 3fa9446270..1b0d00f2d0 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -51,14 +51,14 @@ async function injectRecordScript( }); options = options || {}; await frame.evaluate((options) => { - ((window as unknown) as IWindow).snapshots = []; - const { record, pack } = ((window as unknown) as IWindow).rrweb; + (window as unknown as IWindow).snapshots = []; + const { record, pack } = (window as unknown as IWindow).rrweb; const config: recordOptions = { recordCrossOriginIframes: true, recordCanvas: true, emit(event) { - ((window as unknown) as IWindow).snapshots.push(event); - ((window as unknown) as IWindow).emit(event); + (window as unknown as IWindow).snapshots.push(event); + (window as unknown as IWindow).emit(event); }, }; if (options.usePackFn) { @@ -148,21 +148,21 @@ describe('cross origin iframes', function (this: ISuite) { const frame = await el.contentFrame(); const events = await frame?.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); expect(events).toMatchObject([]); }); it('will emit events if it is in the top level iframe', async () => { const events = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); expect(events.length).not.toBe(0); }); it('should emit contents of iframe', async () => { const events = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); await waitForRAF(ctx.page); // two events (full snapshot + meta) from main frame, and one full snapshot from iframe @@ -171,7 +171,7 @@ describe('cross origin iframes', function (this: ISuite) { it('should emit full snapshot event from iframe as mutation event', async () => { const events = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); await waitForRAF(ctx.page); // two events from main frame, and two from iframe @@ -193,7 +193,7 @@ describe('cross origin iframes', function (this: ISuite) { it('should use unique id for child of iframes', async () => { const events: eventWithTime[] = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); await waitForRAF(ctx.page); expect( @@ -211,7 +211,7 @@ describe('cross origin iframes', function (this: ISuite) { await injectRecordScript(ctx.page.mainFrame().childFrames()[0]); // injects script into new iframe const events: eventWithTime[] = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); expect( (events[events.length - 1].data as mutationData).removes, @@ -343,7 +343,7 @@ describe('cross origin iframes', function (this: ISuite) { it('should record custom events', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; await frame.evaluate(() => { - ((window as unknown) as IWindow).rrweb.addCustomEvent('test', { + (window as unknown as IWindow).rrweb.addCustomEvent('test', { id: 1, parentId: 1, nextId: 2, @@ -388,18 +388,17 @@ describe('cross origin iframes', function (this: ISuite) { }); await waitForRAF(ctx.page); await ctx.page.evaluate(() => { - (document.adoptedStyleSheets![0] - .cssRules[0] as CSSStyleRule).style.setProperty('color', 'green'); - (document.adoptedStyleSheets![0] - .cssRules[0] as CSSStyleRule).style.removeProperty('display'); + ( + document.adoptedStyleSheets![0].cssRules[0] as CSSStyleRule + ).style.setProperty('color', 'green'); + ( + document.adoptedStyleSheets![0].cssRules[0] as CSSStyleRule + ).style.removeProperty('display'); }); await frame.evaluate(() => { - (document.adoptedStyleSheets![0] - .cssRules[0] as CSSStyleRule).style.setProperty( - 'font-size', - 'medium', - 'important', - ); + ( + document.adoptedStyleSheets![0].cssRules[0] as CSSStyleRule + ).style.setProperty('font-size', 'medium', 'important'); document.adoptedStyleSheets![0].insertRule('h2 { color: red; }'); }); await waitForRAF(ctx.page); @@ -445,8 +444,9 @@ describe('cross origin iframes', function (this: ISuite) { 'color', 'green', ); - (document.styleSheets[0] - .cssRules[0] as CSSStyleRule).style.removeProperty('display'); + ( + document.styleSheets[0].cssRules[0] as CSSStyleRule + ).style.removeProperty('display'); }); await frame.evaluate(() => { (document.styleSheets[0].cssRules[0] as CSSStyleRule).style.setProperty( @@ -596,7 +596,7 @@ describe('same origin iframes', function (this: ISuite) { it('should emit contents of iframe once', async () => { const events = await ctx.page.evaluate( - () => ((window as unknown) as IWindow).snapshots, + () => (window as unknown as IWindow).snapshots, ); await waitForRAF(ctx.page); // two events (full snapshot + meta) from main frame, diff --git a/packages/rrweb/test/record/webgl.test.ts b/packages/rrweb/test/record/webgl.test.ts index c9dc782b4a..762d3607bd 100644 --- a/packages/rrweb/test/record/webgl.test.ts +++ b/packages/rrweb/test/record/webgl.test.ts @@ -64,13 +64,13 @@ const setup = function ( ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); await ctx.page.evaluate((canvasSample) => { - const { record } = ((window as unknown) as IWindow).rrweb; + const { record } = (window as unknown as IWindow).rrweb; record({ recordCanvas: true, sampling: { canvas: canvasSample, }, - emit: ((window as unknown) as IWindow).emit, + emit: (window as unknown as IWindow).emit, }); }, canvasSample); }); diff --git a/packages/rrweb/test/replay/webgl-mutation.test.ts b/packages/rrweb/test/replay/webgl-mutation.test.ts index f1ca523c77..378b0636e6 100644 --- a/packages/rrweb/test/replay/webgl-mutation.test.ts +++ b/packages/rrweb/test/replay/webgl-mutation.test.ts @@ -22,9 +22,9 @@ describe('webglMutation', () => { const createShaderMock = jest.fn().mockImplementation(() => { return new WebGLShader(); }); - const context = ({ + const context = { createShader: createShaderMock, - } as unknown) as WebGLRenderingContext; + } as unknown as WebGLRenderingContext; jest.spyOn(canvas, 'getContext').mockImplementation(() => { return context; }); diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 71b85956ea..080c3fcce1 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -597,7 +597,9 @@ describe('replayer', function () { expect( await iframeTwoDocument!.evaluate( (iframe) => (iframe as HTMLIFrameElement)!.contentDocument!.doctype, - (await iframeTwoDocument!.$$('iframe'))[1], + ( + await iframeTwoDocument!.$$('iframe') + )[1], ), ).not.toBeNull(); }); diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 0ece25e5d0..12fa37517c 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -17,7 +17,7 @@ import * as url from 'url'; import * as fs from 'fs'; export async function launchPuppeteer( - options?: Parameters[0], + options?: Parameters<(typeof puppeteer)['launch']>[0], ) { return await puppeteer.launch({ headless: process.env.PUPPETEER_HEADLESS ? true : false, @@ -119,7 +119,8 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { s.data.href = 'about:blank'; } // FIXME: travis coordinates seems different with my laptop - const coordinatesReg = /(bottom|top|left|right|width|height): \d+(\.\d+)?px/g; + const coordinatesReg = + /(bottom|top|left|right|width|height): \d+(\.\d+)?px/g; if ( s.type === EventType.IncrementalSnapshot && s.data.source === IncrementalSource.MouseInteraction @@ -178,10 +179,8 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { add.node.attributes.rr_dataURL && typeof add.node.attributes.rr_dataURL === 'string' ) { - add.node.attributes.rr_dataURL = add.node.attributes.rr_dataURL.replace( - /,.+$/, - ',...', - ); + add.node.attributes.rr_dataURL = + add.node.attributes.rr_dataURL.replace(/,.+$/, ',...'); } } }); @@ -284,7 +283,7 @@ export function stripBase64(events: eventWithTime[]) { const base64Strings: string[] = []; function walk(obj: T): T { if (!obj || typeof obj !== 'object') return obj; - if (Array.isArray(obj)) return (obj.map((e) => walk(e)) as unknown) as T; + if (Array.isArray(obj)) return obj.map((e) => walk(e)) as unknown as T; const newObj: Partial = {}; for (const prop in obj) { const value = obj[prop]; diff --git a/yarn.lock b/yarn.lock index 0a13a7eded..4b0fc28a22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -420,6 +420,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/runtime@^7.20.1", "@babel/runtime@^7.5.5": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" + integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.15.4" resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz" @@ -490,6 +497,206 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@changesets/apply-release-plan@^6.1.3": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@changesets/apply-release-plan/-/apply-release-plan-6.1.3.tgz#3bcc0bd57ba00d50d20df7d0141f1a9b2134eaf7" + integrity sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/config" "^2.3.0" + "@changesets/get-version-range-type" "^0.3.2" + "@changesets/git" "^2.0.0" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + detect-indent "^6.0.0" + fs-extra "^7.0.1" + lodash.startcase "^4.4.0" + outdent "^0.5.0" + prettier "^2.7.1" + resolve-from "^5.0.0" + semver "^5.4.1" + +"@changesets/assemble-release-plan@^5.2.3": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@changesets/assemble-release-plan/-/assemble-release-plan-5.2.3.tgz#5ce6191c6e193d40b566a7b0e01690cfb106f4db" + integrity sha512-g7EVZCmnWz3zMBAdrcKhid4hkHT+Ft1n0mLussFMcB1dE2zCuwcvGoy9ec3yOgPGF4hoMtgHaMIk3T3TBdvU9g== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.5" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + semver "^5.4.1" + +"@changesets/changelog-git@^0.1.14": + version "0.1.14" + resolved "https://registry.yarnpkg.com/@changesets/changelog-git/-/changelog-git-0.1.14.tgz#852caa7727dcf91497c131d05bc2cd6248532ada" + integrity sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA== + dependencies: + "@changesets/types" "^5.2.1" + +"@changesets/cli@^2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@changesets/cli/-/cli-2.26.0.tgz#f215ddb2b41574ffd0dda9cd77fac927ba048fd3" + integrity sha512-0cbTiDms+ICTVtEwAFLNW0jBNex9f5+fFv3I771nBvdnV/mOjd1QJ4+f8KtVSOrwD9SJkk9xbDkWFb0oXd8d1Q== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/apply-release-plan" "^6.1.3" + "@changesets/assemble-release-plan" "^5.2.3" + "@changesets/changelog-git" "^0.1.14" + "@changesets/config" "^2.3.0" + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.5" + "@changesets/get-release-plan" "^3.0.16" + "@changesets/git" "^2.0.0" + "@changesets/logger" "^0.0.5" + "@changesets/pre" "^1.0.14" + "@changesets/read" "^0.5.9" + "@changesets/types" "^5.2.1" + "@changesets/write" "^0.2.3" + "@manypkg/get-packages" "^1.1.3" + "@types/is-ci" "^3.0.0" + "@types/semver" "^6.0.0" + ansi-colors "^4.1.3" + chalk "^2.1.0" + enquirer "^2.3.0" + external-editor "^3.1.0" + fs-extra "^7.0.1" + human-id "^1.0.2" + is-ci "^3.0.1" + meow "^6.0.0" + outdent "^0.5.0" + p-limit "^2.2.0" + preferred-pm "^3.0.0" + resolve-from "^5.0.0" + semver "^5.4.1" + spawndamnit "^2.0.0" + term-size "^2.1.0" + tty-table "^4.1.5" + +"@changesets/config@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@changesets/config/-/config-2.3.0.tgz#bff074d6492fa772cee139f9a04efa4cd56445bb" + integrity sha512-EgP/px6mhCx8QeaMAvWtRrgyxW08k/Bx2tpGT+M84jEdX37v3VKfh4Cz1BkwrYKuMV2HZKeHOh8sHvja/HcXfQ== + dependencies: + "@changesets/errors" "^0.1.4" + "@changesets/get-dependents-graph" "^1.3.5" + "@changesets/logger" "^0.0.5" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + fs-extra "^7.0.1" + micromatch "^4.0.2" + +"@changesets/errors@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@changesets/errors/-/errors-0.1.4.tgz#f79851746c43679a66b383fdff4c012f480f480d" + integrity sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q== + dependencies: + extendable-error "^0.1.5" + +"@changesets/get-dependents-graph@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.5.tgz#f94c6672d2f9a87aa35512eea74550585ba41c21" + integrity sha512-w1eEvnWlbVDIY8mWXqWuYE9oKhvIaBhzqzo4ITSJY9hgoqQ3RoBqwlcAzg11qHxv/b8ReDWnMrpjpKrW6m1ZTA== + dependencies: + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + chalk "^2.1.0" + fs-extra "^7.0.1" + semver "^5.4.1" + +"@changesets/get-release-plan@^3.0.16": + version "3.0.16" + resolved "https://registry.yarnpkg.com/@changesets/get-release-plan/-/get-release-plan-3.0.16.tgz#5d9cfc4ffda02c496ef0fde407210de8e3a0fb19" + integrity sha512-OpP9QILpBp1bY2YNIKFzwigKh7Qe9KizRsZomzLe6pK8IUo8onkAAVUD8+JRKSr8R7d4+JRuQrfSSNlEwKyPYg== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/assemble-release-plan" "^5.2.3" + "@changesets/config" "^2.3.0" + "@changesets/pre" "^1.0.14" + "@changesets/read" "^0.5.9" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + +"@changesets/get-version-range-type@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz#8131a99035edd11aa7a44c341cbb05e668618c67" + integrity sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg== + +"@changesets/git@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@changesets/git/-/git-2.0.0.tgz#8de57649baf13a86eb669a25fa51bcad5cea517f" + integrity sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + is-subdir "^1.1.1" + micromatch "^4.0.2" + spawndamnit "^2.0.0" + +"@changesets/logger@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@changesets/logger/-/logger-0.0.5.tgz#68305dd5a643e336be16a2369cb17cdd8ed37d4c" + integrity sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw== + dependencies: + chalk "^2.1.0" + +"@changesets/parse@^0.3.16": + version "0.3.16" + resolved "https://registry.yarnpkg.com/@changesets/parse/-/parse-0.3.16.tgz#f8337b70aeb476dc81745ab3294022909bc4a84a" + integrity sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg== + dependencies: + "@changesets/types" "^5.2.1" + js-yaml "^3.13.1" + +"@changesets/pre@^1.0.14": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@changesets/pre/-/pre-1.0.14.tgz#9df73999a4d15804da7381358d77bb37b00ddf0f" + integrity sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/errors" "^0.1.4" + "@changesets/types" "^5.2.1" + "@manypkg/get-packages" "^1.1.3" + fs-extra "^7.0.1" + +"@changesets/read@^0.5.9": + version "0.5.9" + resolved "https://registry.yarnpkg.com/@changesets/read/-/read-0.5.9.tgz#a1b63a82b8e9409738d7a0f9cc39b6d7c28cbab0" + integrity sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/git" "^2.0.0" + "@changesets/logger" "^0.0.5" + "@changesets/parse" "^0.3.16" + "@changesets/types" "^5.2.1" + chalk "^2.1.0" + fs-extra "^7.0.1" + p-filter "^2.1.0" + +"@changesets/types@^4.0.1": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0" + integrity sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== + +"@changesets/types@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@changesets/types/-/types-5.2.1.tgz#a228c48004aa8a93bce4be2d1d31527ef3bf21f6" + integrity sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg== + +"@changesets/write@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@changesets/write/-/write-0.2.3.tgz#baf6be8ada2a67b9aba608e251bfea4fdc40bc63" + integrity sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw== + dependencies: + "@babel/runtime" "^7.20.1" + "@changesets/types" "^5.2.1" + fs-extra "^7.0.1" + human-id "^1.0.2" + prettier "^2.7.1" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1628,6 +1835,28 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@manypkg/find-root@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@manypkg/find-root/-/find-root-1.1.0.tgz#a62d8ed1cd7e7d4c11d9d52a8397460b5d4ad29f" + integrity sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA== + dependencies: + "@babel/runtime" "^7.5.5" + "@types/node" "^12.7.1" + find-up "^4.1.0" + fs-extra "^8.1.0" + +"@manypkg/get-packages@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@manypkg/get-packages/-/get-packages-1.1.3.tgz#e184db9bba792fa4693de4658cfb1463ac2c9c47" + integrity sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A== + dependencies: + "@babel/runtime" "^7.5.5" + "@changesets/types" "^4.0.1" + "@manypkg/find-root" "^1.1.0" + fs-extra "^8.1.0" + globby "^11.0.0" + read-yaml-file "^1.1.0" + "@mdn/browser-compat-data@^3.3.14": version "3.3.14" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-3.3.14.tgz#b72a37c654e598f9ae6f8335faaee182bebc6b28" @@ -2169,6 +2398,13 @@ "@types/through" "*" rxjs "^7.2.0" +"@types/is-ci@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3" + integrity sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ== + dependencies: + ci-info "^3.1.0" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" @@ -2252,6 +2488,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/node@^12.7.1": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + "@types/node@^17.0.21": version "17.0.21" resolved "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz" @@ -2332,6 +2573,11 @@ dependencies: "@types/node" "*" +"@types/semver@^6.0.0": + version "6.2.3" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.3.tgz#5798ecf1bec94eaa64db39ee52808ec0693315aa" + integrity sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" @@ -2691,6 +2937,11 @@ alphanum-sort@^1.0.0: resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= +ansi-colors@^4.1.1, ansi-colors@^4.1.3: + 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@^4.2.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" @@ -2846,6 +3097,16 @@ array-unique@^0.2.1: resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= +array.prototype.flat@^1.2.3: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" @@ -2890,6 +3151,11 @@ at-least-node@^1.0.0: resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" @@ -3049,6 +3315,13 @@ before-after-hook@^2.2.0: resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +better-path-resolve@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" + integrity sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g== + dependencies: + is-windows "^1.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3129,13 +3402,20 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" +breakword@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/breakword/-/breakword-1.0.5.tgz#fd420a417f55016736b5b615161cae1c8f819810" + integrity sha512-ex5W9DoOQ/LUEU3PMdLs9ua/CYZl1678NUkKOdUSi8Aw5F1idieaiRURCBFJCwVcrD1J8Iy3vfWSloaMwO2qFg== + dependencies: + wcwidth "^1.0.1" + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" @@ -3366,7 +3646,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3383,7 +3663,7 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.0: +chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3460,6 +3740,11 @@ ci-info@^2.0.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + ci-info@^3.1.1: version "3.2.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz" @@ -3509,6 +3794,15 @@ cli-width@^4.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -3518,6 +3812,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" @@ -3902,6 +4205,15 @@ cross-fetch@3.1.5: dependencies: node-fetch "2.6.7" +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" @@ -4108,6 +4420,31 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +csv-generate@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" + integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== + +csv-parse@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" + integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== + +csv-stringify@^5.6.5: + version "5.6.5" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00" + integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== + +csv@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d" + integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== + dependencies: + csv-generate "^3.4.3" + csv-parse "^4.16.3" + csv-stringify "^5.6.5" + stream-transform "^2.1.3" + dargs@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" @@ -4222,6 +4559,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" @@ -4478,6 +4823,13 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +enquirer@^2.3.0: + 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" @@ -4563,11 +4915,66 @@ es-abstract@^1.18.0-next.2: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + es-module-lexer@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" @@ -5193,7 +5600,12 @@ extend@~3.0.2: resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: +extendable-error@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/extendable-error/-/extendable-error-0.1.7.tgz#60b9adf206264ac920058a7395685ae4670c2b96" + integrity sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg== + +external-editor@^3.0.3, external-editor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== @@ -5429,6 +5841,14 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-yarn-workspace-root2@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" + integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== + dependencies: + micromatch "^4.0.2" + pkg-dir "^4.2.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" @@ -5517,6 +5937,24 @@ fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^7.0.1, fs-extra@~7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" @@ -5527,15 +5965,6 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@~7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" @@ -5565,11 +5994,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" @@ -5596,7 +6040,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -5619,6 +6063,15 @@ get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" @@ -5795,7 +6248,14 @@ globals@^13.15.0, globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.1, globby@^11.1.0: +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -5835,6 +6295,13 @@ glur@^1.1.2: resolved "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz" integrity sha1-8g6jbbEDv8KSNDkh8fkeg8NGdok= +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.2.6" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" @@ -5845,11 +6312,21 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.1.5: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + graceful-fs@^4.2.9: version "4.2.9" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + handlebars@^4.7.6: version "4.7.7" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" @@ -5897,6 +6374,11 @@ has-bigints@^1.0.1: resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" @@ -5917,6 +6399,18 @@ has-own-prop@^2.0.0: resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af" integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" @@ -6045,6 +6539,11 @@ https-proxy-agent@5.0.1: agent-base "6" debug "4" +human-id@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/human-id/-/human-id-1.0.2.tgz#e654d4b2b0d8b07e45da9f6020d8af17ec0a5df3" + integrity sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" @@ -6273,6 +6772,15 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + side-channel "^1.0.4" + ip@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" @@ -6288,6 +6796,15 @@ is-absolute-url@^2.0.0: resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" @@ -6332,6 +6849,11 @@ is-callable@^1.2.4: resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz" @@ -6346,6 +6868,13 @@ is-ci@^3.0.0: dependencies: ci-info "^3.1.1" +is-ci@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + is-color-stop@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" @@ -6472,6 +7001,11 @@ is-negative-zero@^2.0.1: resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + is-number-object@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz" @@ -6569,6 +7103,13 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-ssh@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" @@ -6593,6 +7134,13 @@ is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-subdir@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-subdir/-/is-subdir-1.2.0.tgz#b791cd28fab5202e91a08280d51d9d7254fd20d4" + integrity sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw== + dependencies: + better-path-resolve "1.0.0" + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" @@ -6607,6 +7155,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" @@ -6624,6 +7183,18 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-windows@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" @@ -7626,7 +8197,7 @@ js-tokens@^4.0.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.6.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7814,6 +8385,11 @@ kleur@^3.0.0, kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kleur@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + kolorist@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.6.0.tgz#f43ac794305b30032a5bedcae7799d0f91d2ff36" @@ -7932,6 +8508,16 @@ load-json-file@^6.2.0: strip-bom "^4.0.0" type-fest "^0.6.0" +load-yaml-file@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" + integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.13.0" + pify "^4.0.1" + strip-bom "^3.0.0" + loader-utils@^1.1.0: version "1.4.2" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" @@ -8003,6 +8589,11 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz" @@ -8036,6 +8627,14 @@ log-symbols@^5.1.0: chalk "^5.0.0" is-unicode-supported "^1.1.0" +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -8194,6 +8793,23 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +meow@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.1.tgz#1ad64c4b76b2a24dfb2f635fddcadf320d251467" + integrity sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" + meow@^7.0.0, meow@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz" @@ -8267,6 +8883,14 @@ micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^4.0.2: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + micromatch@^4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" @@ -8352,7 +8976,7 @@ minimatch@~3.0.5: dependencies: brace-expansion "^1.1.7" -minimist-options@4.1.0: +minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== @@ -8453,6 +9077,11 @@ mitt@^3.0.0: resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +mixme@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.5.tgz#bf8f67d8caf10fdb49fd23198fd1fa6d8e406627" + integrity sha512-/6IupbRx32s7jjEwHcycXikJwFD5UujbVNuJFkeKLYje+92OvtuPniF6JhnFm5JCTDUhS+kYK3W/4BWYQYXz7w== + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" @@ -8828,6 +9457,11 @@ object-inspect@^1.10.3, object-inspect@^1.11.0: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz" integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== +object-inspect@^1.12.2: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" @@ -8848,6 +9482,16 @@ object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz" @@ -8966,6 +9610,18 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +outdent@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" + integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== + +p-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" + integrity sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== + dependencies: + p-map "^2.0.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" @@ -9018,6 +9674,11 @@ p-map-series@^2.1.0: resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz" integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + p-map@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" @@ -9262,6 +9923,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" @@ -9720,6 +10386,16 @@ postcss@^8.4.18: picocolors "^1.0.0" source-map-js "^1.0.2" +preferred-pm@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" + integrity sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ== + dependencies: + find-up "^5.0.0" + find-yarn-workspace-root2 "1.2.16" + path-exists "^4.0.0" + which-pm "2.0.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -9740,6 +10416,11 @@ prettier@2.2.1: resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@^2.7.1: + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== + pretty-format@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz" @@ -9838,6 +10519,11 @@ proxy-from-env@1.1.0, proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" @@ -10067,6 +10753,16 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +read-yaml-file@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-yaml-file/-/read-yaml-file-1.1.0.tgz#9362bbcbdc77007cc8ea4519fe1c0b821a7ce0d8" + integrity sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.6.1" + pify "^4.0.1" + strip-bom "^3.0.0" + read@1, read@~1.0.1: version "1.0.7" resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" @@ -10133,6 +10829,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz" @@ -10140,6 +10841,15 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" @@ -10191,6 +10901,11 @@ require-directory@^2.1.1: resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + require-relative@^0.8.7: version "0.8.7" resolved "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz" @@ -10498,6 +11213,15 @@ safe-identifier@^0.4.1: resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -10522,7 +11246,7 @@ semver-match@0.1.1: dependencies: semver "^5.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10589,7 +11313,7 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" -set-blocking@~2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -10702,6 +11426,18 @@ smart-buffer@^4.1.0: resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +smartwrap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/smartwrap/-/smartwrap-2.0.2.tgz#7e25d3dd58b51c6ca4aba3a9e391650ea62698a4" + integrity sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA== + dependencies: + array.prototype.flat "^1.2.3" + breakword "^1.0.5" + grapheme-splitter "^1.0.4" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + yargs "^15.1.0" + socks-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz" @@ -10771,6 +11507,14 @@ spawn-command@^0.0.2-1: resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= +spawndamnit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawndamnit/-/spawndamnit-2.0.0.tgz#9f762ac5c3476abb994b42ad592b5ad22bb4b0ad" + integrity sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA== + dependencies: + cross-spawn "^5.1.0" + signal-exit "^3.0.2" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" @@ -10872,6 +11616,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +stream-transform@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3" + integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== + dependencies: + mixme "^0.5.1" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -10921,6 +11672,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-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10938,6 +11698,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" @@ -10946,6 +11715,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" @@ -11207,6 +11985,11 @@ temp-write@^4.0.0: temp-dir "^1.0.0" uuid "^3.3.2" +term-size@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" @@ -11460,6 +12243,19 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tty-table@^4.1.5: + version "4.1.6" + resolved "https://registry.yarnpkg.com/tty-table/-/tty-table-4.1.6.tgz#6bd58338f36c94cce478c3337934d8a65ab40a73" + integrity sha512-kRj5CBzOrakV4VRRY5kUWbNYvo/FpOsz65DzI5op9P+cHov3+IqPbo1JE1ZnQGkHdZgNFDsrEjrfqqy/Ply9fw== + dependencies: + chalk "^4.1.2" + csv "^5.5.0" + kleur "^4.1.4" + smartwrap "^2.0.2" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + yargs "^17.1.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" @@ -11623,6 +12419,15 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" @@ -11675,6 +12480,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unbzip2-stream@1.4.3, unbzip2-stream@^1.3.3: version "1.4.3" resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" @@ -11970,6 +12785,31 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + +which-pm@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" + integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== + dependencies: + load-yaml-file "^0.2.0" + path-exists "^4.0.0" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" @@ -12001,6 +12841,15 @@ wordwrap@^1.0.0: resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -12116,11 +12965,21 @@ xtend@~4.0.1: resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^2.1.2: + version "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: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" @@ -12146,7 +13005,7 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^18.1.3: +yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -12154,6 +13013,28 @@ yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" +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== + +yargs@^15.1.0: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" @@ -12167,6 +13048,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +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== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" From 8d209a62f31c4c80e3e5bc36e47d7282ee854ac7 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 12 Feb 2023 23:22:05 +1100 Subject: [PATCH 123/201] add change log and update readme (#1122) --- .changeset/twenty-lies-switch.md | 58 ++++++++++++++++++++++++++++++++ README.md | 6 ++-- 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 .changeset/twenty-lies-switch.md diff --git a/.changeset/twenty-lies-switch.md b/.changeset/twenty-lies-switch.md new file mode 100644 index 0000000000..59e52d1e52 --- /dev/null +++ b/.changeset/twenty-lies-switch.md @@ -0,0 +1,58 @@ +--- +'rrweb-snapshot': patch +'@rrweb/types': patch +'rrdom': patch +'rrdom-nodejs': patch +'rrweb': patch +'rrweb-player': patch +--- + +Add missing change logs manually. In the next version, all change logs will be generated automatically. + +- [`a220835`](https://github.com/rrweb-io/rrweb/commit/a220835eeb81ca4f294682e060d46c8853720d7f) [#1053](https://github.com/rrweb-io/rrweb/pull/1053) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Post message can break cross origin iframe recording. + +- [`7e8dcdb`](https://github.com/rrweb-io/rrweb/commit/7e8dcdb11dc5dfefcdd19ff5e13ec9d8b5c24dcc) [#1063](https://github.com/rrweb-io/rrweb/pull/1063) Thanks [@lele0108](https://github.com/lele0108)! - Fix: muted false -> true not being set. + +- [`b655361`](https://github.com/rrweb-io/rrweb/commit/b655361a5f0d50a053fcd0e5c823b8494c33b89c) [#1067](https://github.com/rrweb-io/rrweb/pull/1067) Thanks [@mydea](https://github.com/mydea)! - Export recordOptions type. + +- [`36b44e1`](https://github.com/rrweb-io/rrweb/commit/36b44e104b91fc74c3e69684111240cd23105340) [#1042](https://github.com/rrweb-io/rrweb/pull/1042) Thanks [@wfk007](https://github.com/wfk007)! - Fix: Failed to execute insertBefore on Node. + +- [`44e92cb`](https://github.com/rrweb-io/rrweb/commit/44e92cbff981c36e754dfcb9a184eae9e7292ecf) [#1058](https://github.com/rrweb-io/rrweb/pull/1058) Thanks [@mydea](https://github.com/mydea)! - Handle errors when observing iframes. + +- [`729b8bf`](https://github.com/rrweb-io/rrweb/commit/729b8bf38c8c7f2e1b22b4e0f7cab14f0807bc74) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Catch iframe manager & fix formatting issues. + +- [`03821d9`](https://github.com/rrweb-io/rrweb/commit/03821d9b9fa0513e6e373881d43102ceb9388340) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Harmonize on a single getWindowScroll + +- [`fe69bd6`](https://github.com/rrweb-io/rrweb/commit/fe69bd6456cead304bfc77cf72c9db0f8c030842) [#1087](https://github.com/rrweb-io/rrweb/pull/1087) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs. + +- [`d08913d`](https://github.com/rrweb-io/rrweb/commit/d08913d0dc506dbf119e94686fe5f01c415316c9) [#1086](https://github.com/rrweb-io/rrweb/pull/1086) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: missed adopted style sheets of shadow doms in checkout full snapshot. + +- [`66abe17`](https://github.com/rrweb-io/rrweb/commit/66abe17832dbb23b3948af1c394f9a02caccc17b) [#1032](https://github.com/rrweb-io/rrweb/pull/1032) Thanks [@dbseel](https://github.com/dbseel)! - Fix: isBlocked throws on invalid HTML element. + +- [`07aa1b2`](https://github.com/rrweb-io/rrweb/commit/07aa1b2807da5a9a1db678ebc3ff59320a300d06) [#1049](https://github.com/rrweb-io/rrweb/pull/1049) Thanks [@Juice10](https://github.com/Juice10)! - Fix: shadow dom bugs. + +- [`57a2e14`](https://github.com/rrweb-io/rrweb/commit/57a2e140ea419f7790b1672529f21dfe2261b52b) [#1088](https://github.com/rrweb-io/rrweb/pull/1088) Thanks [@mydea](https://github.com/mydea)! - Fix: Guard against missing window.CSSStyleSheet. + +- [`fc82869`](https://github.com/rrweb-io/rrweb/commit/fc828694099b87b4d811e6b651a7bb4c7499b896) [#1093](https://github.com/rrweb-io/rrweb/pull/1093) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: cross origin iframe bugs. + +- [`a77e302`](https://github.com/rrweb-io/rrweb/commit/a77e30217893e63f8025c73afc3ac1ba294d7761) [#1104](https://github.com/rrweb-io/rrweb/pull/1104) Thanks [@jlalmes](https://github.com/jlalmes)! - [console-plugin] Feat: Record unhandled rejection event. + +- [`25a4f5a`](https://github.com/rrweb-io/rrweb/commit/25a4f5ab6c7311f2e8e5e1a4d232c2820adf910e) [#1115](https://github.com/rrweb-io/rrweb/pull/1115) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Don't trigger Finish event when in liveMode. + +- [`cb15800`](https://github.com/rrweb-io/rrweb/commit/cb1580008d04b0bc5c5d4ebec0e2e79899faaeb6) [#1106](https://github.com/rrweb-io/rrweb/pull/1106) Thanks [@mydea](https://github.com/mydea)! - Fix: Ensure CSS support is checked more robustly. + +- [`0732618`](https://github.com/rrweb-io/rrweb/commit/07326182f9750646771918481f116b946a17c2a9) [#1100](https://github.com/rrweb-io/rrweb/pull/1100) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: wrong rootId value in special iframes. + +- [`3caa25e`](https://github.com/rrweb-io/rrweb/commit/3caa25ed9b19954c98775f22d5fa47233fa3d1db) [#1098](https://github.com/rrweb-io/rrweb/pull/1098) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Refactor: Don't have requestAnimationFrame looping in background for Live Mode. + +- [`3a26e36`](https://github.com/rrweb-io/rrweb/commit/3a26e36f6f625c0391c7e6d3f1050660adfccc4f) [#1092](https://github.com/rrweb-io/rrweb/pull/1092) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: regression of issue: ShadowHost can't be a string (issue 941) + +- [`4ee86fe`](https://github.com/rrweb-io/rrweb/commit/4ee86fe66d3e1fe7071f9c8764d82a6fa5c71d57) [#1091](https://github.com/rrweb-io/rrweb/pull/1091) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness. + +- [`07d22e7`](https://github.com/rrweb-io/rrweb/commit/07d22e7cd999a48e7371aaef1b979574bb746500) [#1111](https://github.com/rrweb-io/rrweb/pull/1111) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: enable to customize logger in the replayer. + +- [`0627d4d`](https://github.com/rrweb-io/rrweb/commit/0627d4df7cc76cde7babbd37ab8e3da5810fb51d) [#1109](https://github.com/rrweb-io/rrweb/pull/1109) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: add option to record on DOMContentLoaded event. + +- [`174b9ac`](https://github.com/rrweb-io/rrweb/commit/174b9ac066565b8c065f40f0303189f10c7c4efb) [#1112](https://github.com/rrweb-io/rrweb/pull/1112) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed. + +- [`5a1e5e9`](https://github.com/rrweb-io/rrweb/commit/5a1e5e919e3f8bef48d142115c0afd5706a442b5) [#1119](https://github.com/rrweb-io/rrweb/pull/1119) Thanks [@Juice10](https://github.com/Juice10)! - Feat: Automate NPM package releases. diff --git a/README.md b/README.md index 90c8c71811..7e81e98957 100644 --- a/README.md +++ b/README.md @@ -88,13 +88,13 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE
From 502d15df9f7f43b3408ccfbb3f14c4bb007883c4 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Sun, 12 Feb 2023 23:25:21 +1100 Subject: [PATCH 124/201] fix: outdated ':hover' styles can't be removed from iframes or shadow doms (#1121) * fix :hover class can't be removed in iframes * add test case and change log --- .changeset/tidy-yaks-joke.md | 5 + packages/rrweb/src/replay/index.ts | 6 +- .../test/events/iframe-shadowdom-hover.ts | 206 ++++++++++++++++++ packages/rrweb/test/replayer.test.ts | 61 ++++++ 4 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 .changeset/tidy-yaks-joke.md create mode 100644 packages/rrweb/test/events/iframe-shadowdom-hover.ts diff --git a/.changeset/tidy-yaks-joke.md b/.changeset/tidy-yaks-joke.md new file mode 100644 index 0000000000..bcd476a850 --- /dev/null +++ b/.changeset/tidy-yaks-joke.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Fix: outdated ':hover' styles can't be removed from iframes or shadow doms diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 396efe76f5..a6591142c0 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -149,6 +149,9 @@ export class Replayer { private mousePos: mouseMovePos | null = null; private touchActive: boolean | null = null; + // Keep the rootNode of the last hovered element. So when hovering a new element, we can remove the last hovered element's :hover style. + private lastHoveredRootNode: Document | ShadowRoot; + // In the fast-forward mode, only the last selection data needs to be applied. private lastSelectionData: selectionData | null = null; @@ -2091,11 +2094,12 @@ export class Replayer { } private hoverElements(el: Element) { - this.iframe.contentDocument + (this.lastHoveredRootNode || this.iframe.contentDocument) ?.querySelectorAll('.\\:hover') .forEach((hoveredEl) => { hoveredEl.classList.remove(':hover'); }); + this.lastHoveredRootNode = el.getRootNode() as Document | ShadowRoot; let currentEl: Element | null = el; while (currentEl) { if (currentEl.classList) { diff --git a/packages/rrweb/test/events/iframe-shadowdom-hover.ts b/packages/rrweb/test/events/iframe-shadowdom-hover.ts new file mode 100644 index 0000000000..0e3671dcbf --- /dev/null +++ b/packages/rrweb/test/events/iframe-shadowdom-hover.ts @@ -0,0 +1,206 @@ +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, + }, + { + type: EventType.FullSnapshot, + 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: 'iframe', + attributes: {}, + childNodes: [], + }, + ], + }, + ], + }, + ], + }, + initialOffset: { top: 0, left: 0 }, + }, + timestamp: now + 200, + }, + // add iframe + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + adds: [ + { + parentId: 6, + nextId: null, + node: { + type: 0, + childNodes: [ + { + type: 1, + name: 'html', + publicId: '', + systemId: '', + rootId: 7, + id: 8, + }, + { + type: 2, + tagName: 'html', + attributes: { lang: 'en' }, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [], + rootId: 7, + id: 10, + }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [ + { + type: 2, + tagName: 'div', + attributes: {}, + childNodes: [], + rootId: 7, + id: 13, + isShadow: true, + }, + ], + isShadowHost: true, + rootId: 7, + id: 12, + }, + { + type: 2, + tagName: 'span', + attributes: {}, + childNodes: [], + rootId: 7, + id: 14, + }, + { type: 3, textContent: '\t\n', rootId: 7, id: 15 }, + ], + rootId: 7, + id: 11, + }, + ], + rootId: 7, + id: 9, + }, + ], + id: 7, + }, + }, + ], + removes: [], + texts: [], + attributes: [], + isAttachIframe: true, + }, + timestamp: now + 500, + }, + // hover element in iframe + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseMove, + positions: [ + { + x: 0, + y: 0, + id: 14, + timeOffset: 0, + }, + ], + }, + timestamp: now + 500, + }, + // hover element in shadow dom + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseMove, + positions: [ + { + x: 0, + y: 0, + id: 13, + timeOffset: 0, + }, + ], + }, + timestamp: now + 1000, + }, + // hover element in iframe again + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.MouseMove, + positions: [ + { + x: 0, + y: 0, + id: 14, + timeOffset: 0, + }, + ], + }, + timestamp: now + 1500, + }, +]; + +export default events; diff --git a/packages/rrweb/test/replayer.test.ts b/packages/rrweb/test/replayer.test.ts index 080c3fcce1..7756710410 100644 --- a/packages/rrweb/test/replayer.test.ts +++ b/packages/rrweb/test/replayer.test.ts @@ -21,6 +21,7 @@ import canvasInIframe from './events/canvas-in-iframe'; import adoptedStyleSheet from './events/adopted-style-sheet'; import adoptedStyleSheetModification from './events/adopted-style-sheet-modification'; import documentReplacementEvents from './events/document-replacement'; +import hoverInIframeShadowDom from './events/iframe-shadowdom-hover'; import { ReplayerEvents } from '@rrweb/types'; interface ISuite { @@ -1015,4 +1016,64 @@ describe('replayer', function () { // No errors should be thrown. expect(errorThrown).not.toHaveBeenCalled(); }); + + it('should remove outdated hover styles in iframes and shadow doms', async () => { + await page.evaluate(`events = ${JSON.stringify(hoverInIframeShadowDom)}`); + + await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(550); + `); + const replayerIframe = await page.$('iframe'); + const contentDocument = await replayerIframe!.contentFrame()!; + const iframe = await contentDocument!.$('iframe'); + expect(iframe).not.toBeNull(); + const docInIFrame = await iframe?.contentFrame(); + expect(docInIFrame).not.toBeNull(); + + // hover element in iframe at 500ms + expect( + await docInIFrame?.evaluate( + () => document.querySelector('span')?.className, + ), + ).toBe(':hover'); + // At this time, there should be no class name in shadow dom + expect( + await docInIFrame?.evaluate(() => { + const shadowRoot = document.querySelector('div')?.shadowRoot; + return (shadowRoot?.childNodes[0] as HTMLElement).className; + }), + ).toBe(''); + + // hover element in shadow dom at 1000ms + await page.evaluate('replayer.pause(1050);'); + // :hover style should be removed from iframe + expect( + await docInIFrame?.evaluate( + () => document.querySelector('span')?.className, + ), + ).toBe(''); + expect( + await docInIFrame?.evaluate(() => { + const shadowRoot = document.querySelector('div')?.shadowRoot; + return (shadowRoot?.childNodes[0] as HTMLElement).className; + }), + ).toBe(':hover'); + + // hover element in iframe at 1500ms again + await page.evaluate('replayer.pause(1550);'); + // hover style should be removed from shadow dom + expect( + await docInIFrame?.evaluate(() => { + const shadowRoot = document.querySelector('div')?.shadowRoot; + return (shadowRoot?.childNodes[0] as HTMLElement).className; + }), + ).toBe(''); + expect( + await docInIFrame?.evaluate( + () => document.querySelector('span')?.className, + ), + ).toBe(':hover'); + }); }); From fdce994018768672463717f3b4d9bbd98c33f507 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sun, 12 Feb 2023 13:39:52 +0100 Subject: [PATCH 125/201] Add friendly changelog message (#1124) * Add friendly changelog message * add empty changelog --- .changeset/beige-numbers-enjoy.md | 2 ++ .changeset/config.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .changeset/beige-numbers-enjoy.md diff --git a/.changeset/beige-numbers-enjoy.md b/.changeset/beige-numbers-enjoy.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/beige-numbers-enjoy.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/config.json b/.changeset/config.json index d9d5734926..0b0f087c53 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", - "changelog": "@changesets/cli/changelog", + "changelog": ["@changesets/changelog-github", { "repo": "rrweb-io/rrweb" }], "commit": false, "fixed": [], "linked": [], From 2946f102ffbef98417b6541e2266f8f819191530 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sun, 12 Feb 2023 13:50:28 +0100 Subject: [PATCH 126/201] Add needed dependency for changeset formatter (#1125) --- .changeset/olive-worms-pump.md | 2 ++ package.json | 1 + yarn.lock | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 .changeset/olive-worms-pump.md diff --git a/.changeset/olive-worms-pump.md b/.changeset/olive-worms-pump.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/olive-worms-pump.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/package.json b/package.json index 2245470f48..8726842fab 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "packages/*" ], "devDependencies": { + "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.0", "@monorepo-utils/workspaces-to-typescript-project-references": "^2.8.2", "@typescript-eslint/eslint-plugin": "^5.25.0", diff --git a/yarn.lock b/yarn.lock index 4b0fc28a22..eef747b04e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -535,6 +535,15 @@ dependencies: "@changesets/types" "^5.2.1" +"@changesets/changelog-github@^0.4.8": + version "0.4.8" + resolved "https://registry.yarnpkg.com/@changesets/changelog-github/-/changelog-github-0.4.8.tgz#b7f8ae85d0c0ff08028d924c5e59a1cbd3742634" + integrity sha512-jR1DHibkMAb5v/8ym77E4AMNWZKB5NPzw5a5Wtqm1JepAuIF+hrKp2u04NKM14oBZhHglkCfrla9uq8ORnK/dw== + dependencies: + "@changesets/get-github-info" "^0.5.2" + "@changesets/types" "^5.2.1" + dotenv "^8.1.0" + "@changesets/cli@^2.26.0": version "2.26.0" resolved "https://registry.yarnpkg.com/@changesets/cli/-/cli-2.26.0.tgz#f215ddb2b41574ffd0dda9cd77fac927ba048fd3" @@ -605,6 +614,14 @@ fs-extra "^7.0.1" semver "^5.4.1" +"@changesets/get-github-info@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@changesets/get-github-info/-/get-github-info-0.5.2.tgz#0cde2cadba57db85c714dc303c077da919a574e5" + integrity sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg== + dependencies: + dataloader "^1.4.0" + node-fetch "^2.5.0" + "@changesets/get-release-plan@^3.0.16": version "3.0.16" resolved "https://registry.yarnpkg.com/@changesets/get-release-plan/-/get-release-plan-3.0.16.tgz#5d9cfc4ffda02c496ef0fde407210de8e3a0fb19" @@ -4466,6 +4483,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + date-fns@^2.16.1: version "2.28.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" @@ -4741,6 +4763,11 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" +dotenv@^8.1.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + duplexer@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" @@ -9193,6 +9220,13 @@ node-fetch@2.6.7, node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.5.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + node-gyp@^5.0.2: version "5.1.1" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz" From 1385f7acc0052f83be1458a7b00e18c026ee393f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 13 Feb 2023 00:00:18 +1100 Subject: [PATCH 127/201] fix duplicated shadow dom (#1095) * fix: duplicated elements in shadow doms clean observers before taking new full snapshots * add checker for replayer to make it stable when data has duplicated nodes * apply review suggestions * add change log * Apply formatting changes * update prettier to fit the master branch --------- Co-authored-by: Mark-Fenng --- .changeset/loud-seals-raise.md | 6 + package.json | 1 + packages/rrweb-snapshot/src/rebuild.ts | 15 +- packages/rrweb-snapshot/src/utils.ts | 33 ++++ packages/rrweb-snapshot/test/utils.test.ts | 150 ++++++++++++++++++ packages/rrweb/package.json | 2 - packages/rrweb/src/record/index.ts | 4 +- .../rrweb/src/record/shadow-dom-manager.ts | 139 ++++++++-------- yarn.lock | 12 +- 9 files changed, 281 insertions(+), 81 deletions(-) create mode 100644 .changeset/loud-seals-raise.md create mode 100644 packages/rrweb-snapshot/test/utils.test.ts diff --git a/.changeset/loud-seals-raise.md b/.changeset/loud-seals-raise.md new file mode 100644 index 0000000000..fb7309d243 --- /dev/null +++ b/.changeset/loud-seals-raise.md @@ -0,0 +1,6 @@ +--- +'rrweb-snapshot': patch +'rrweb': patch +--- + +Fix duplicated shadow doms diff --git a/package.json b/package.json index 8726842fab..6246830b06 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "lerna": "^4.0.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", + "prettier": "2.8.4", "turbo": "^1.2.4", "typescript": "^4.7.3" }, diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index bb9e61d645..854d6c957b 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -8,7 +8,7 @@ import { attributes, legacyAttributes, } from './types'; -import { isElement, Mirror } from './utils'; +import { isElement, Mirror, isNodeMetaEqual } from './utils'; const tagMap: tagMap = { script: 'noscript', @@ -364,6 +364,19 @@ export function buildNodeWithSN( afterAppend, cache, } = options; + /** + * Add a check to see if the node is already in the mirror. If it is, we can skip the whole process. + * This situation (duplicated nodes) can happen when recorder has some unfixed bugs and the same node is recorded twice. Or something goes wrong when saving or transferring event data. + * Duplicated node creation may cause unexpected errors in replayer. This check tries best effort to prevent the errors. + */ + if (mirror.has(n.id)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const nodeInMirror = mirror.getNode(n.id)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const meta = mirror.getMeta(nodeInMirror)!; + // For safety concern, check if the node in mirror is the same as the node we are trying to build + if (isNodeMetaEqual(meta, n)) return mirror.getNode(n.id); + } let node = buildNode(n, { doc, hackCss, cache }); if (!node) { return null; diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index d505ad0353..1c8c757363 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -5,6 +5,12 @@ import { nodeMetaMap, IMirror, serializedNodeWithId, + serializedNode, + NodeType, + documentNode, + documentTypeNode, + textNode, + elementNode, } from './types'; export function isElement(n: Node): n is Element { @@ -213,3 +219,30 @@ export function is2DCanvasBlank(canvas: HTMLCanvasElement): boolean { } return true; } + +export function isNodeMetaEqual(a: serializedNode, b: serializedNode): boolean { + if (!a || !b || a.type !== b.type) return false; + if (a.type === NodeType.Document) + return a.compatMode === (b as documentNode).compatMode; + else if (a.type === NodeType.DocumentType) + return ( + a.name === (b as documentTypeNode).name && + a.publicId === (b as documentTypeNode).publicId && + a.systemId === (b as documentTypeNode).systemId + ); + else if ( + a.type === NodeType.Comment || + a.type === NodeType.Text || + a.type === NodeType.CDATA + ) + return a.textContent === (b as textNode).textContent; + else if (a.type === NodeType.Element) + return ( + a.tagName === (b as elementNode).tagName && + JSON.stringify(a.attributes) === + JSON.stringify((b as elementNode).attributes) && + a.isSVG === (b as elementNode).isSVG && + a.needBlock === (b as elementNode).needBlock + ); + return false; +} diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts new file mode 100644 index 0000000000..09a2bebd4d --- /dev/null +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -0,0 +1,150 @@ +/** + * @jest-environment jsdom + */ +import { NodeType, serializedNode } from '../src/types'; +import { isNodeMetaEqual } from '../src/utils'; +import { serializedNodeWithId } from 'rrweb-snapshot'; + +describe('utils', () => { + describe('isNodeMetaEqual()', () => { + const document1: serializedNode = { + type: NodeType.Document, + compatMode: 'CSS1Compat', + childNodes: [], + }; + const document2: serializedNode = { + type: NodeType.Document, + compatMode: 'BackCompat', + childNodes: [], + }; + const documentType1: serializedNode = { + type: NodeType.DocumentType, + name: 'html', + publicId: '', + systemId: '', + }; + const documentType2: serializedNode = { + type: NodeType.DocumentType, + name: 'html', + publicId: '', + systemId: 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd', + }; + const text1: serializedNode = { + type: NodeType.Text, + textContent: 'Hello World', + }; + const text2: serializedNode = { + type: NodeType.Text, + textContent: 'Hello world', + }; + const comment1: serializedNode = { + type: NodeType.Comment, + textContent: 'Hello World', + }; + const comment2: serializedNode = { + type: NodeType.Comment, + textContent: 'Hello world', + }; + const element1: serializedNode = { + type: NodeType.Element, + tagName: 'div', + attributes: { + className: 'test', + }, + childNodes: [], + }; + const element2: serializedNode = { + type: NodeType.Element, + tagName: 'span', + attributes: { + 'aria-label': 'Hello World', + }, + childNodes: [], + }; + const element3: serializedNode = { + type: NodeType.Element, + tagName: 'div', + attributes: { id: 'test' }, + childNodes: [comment1 as serializedNodeWithId], + }; + + it('should return false if two nodes have different node types', () => { + expect( + isNodeMetaEqual( + undefined as unknown as serializedNode, + null as unknown as serializedNode, + ), + ).toBeFalsy(); + expect(isNodeMetaEqual(document1, element1)).toBeFalsy(); + expect(isNodeMetaEqual(document1, documentType1)).toBeFalsy(); + expect(isNodeMetaEqual(documentType1, element1)).toBeFalsy(); + expect(isNodeMetaEqual(text1, comment1)).toBeFalsy(); + expect(isNodeMetaEqual(text1, element1)).toBeFalsy(); + expect(isNodeMetaEqual(comment1, element1)).toBeFalsy(); + }); + + it('should compare meta data of two document nodes', () => { + expect( + isNodeMetaEqual(document1, JSON.parse(JSON.stringify(document1))), + ).toBeTruthy(); + expect( + isNodeMetaEqual(JSON.parse(JSON.stringify(document2)), document2), + ).toBeTruthy(); + expect(isNodeMetaEqual(document1, document2)).toBeFalsy(); + }); + + it('should compare meta data of two documentType nodes', () => { + expect( + isNodeMetaEqual( + documentType1, + JSON.parse(JSON.stringify(documentType1)), + ), + ).toBeTruthy(); + expect( + isNodeMetaEqual( + JSON.parse(JSON.stringify(documentType2)), + documentType2, + ), + ).toBeTruthy(); + expect(isNodeMetaEqual(documentType1, documentType2)).toBeFalsy(); + }); + + it('should compare meta data of two text nodes', () => { + expect( + isNodeMetaEqual(text1, JSON.parse(JSON.stringify(text1))), + ).toBeTruthy(); + expect( + isNodeMetaEqual(JSON.parse(JSON.stringify(text2)), text2), + ).toBeTruthy(); + expect(isNodeMetaEqual(text1, text2)).toBeFalsy(); + }); + + it('should compare meta data of two comment nodes', () => { + expect( + isNodeMetaEqual(comment1, JSON.parse(JSON.stringify(comment1))), + ).toBeTruthy(); + expect( + isNodeMetaEqual(JSON.parse(JSON.stringify(comment2)), comment2), + ).toBeTruthy(); + expect(isNodeMetaEqual(comment1, comment2)).toBeFalsy(); + }); + + it('should compare meta data of two HTML elements', () => { + expect( + isNodeMetaEqual(element1, JSON.parse(JSON.stringify(element1))), + ).toBeTruthy(); + expect( + isNodeMetaEqual(JSON.parse(JSON.stringify(element2)), element2), + ).toBeTruthy(); + expect( + isNodeMetaEqual(element1, { + ...element1, + childNodes: [comment2 as serializedNodeWithId], + }), + ).toBeTruthy(); + expect(isNodeMetaEqual(element1, element2)).toBeFalsy(); + expect(isNodeMetaEqual(element1, element3)).toBeFalsy(); + expect(isNodeMetaEqual(element2, element3)).toBeFalsy(); + }); + }); +}); diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 85117bdb56..f0b9bc4232 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -53,7 +53,6 @@ "@types/jest-image-snapshot": "^5.1.0", "@types/node": "^17.0.21", "@types/offscreencanvas": "^2019.6.4", - "@types/prettier": "^2.3.2", "@types/puppeteer": "^5.4.4", "construct-style-sheets-polyfill": "^3.1.0", "cross-env": "^5.2.0", @@ -65,7 +64,6 @@ "jest": "^27.5.1", "jest-image-snapshot": "^5.2.0", "jest-snapshot": "^23.6.0", - "prettier": "2.2.1", "puppeteer": "^11.0.0", "rollup": "^2.68.0", "rollup-plugin-esbuild": "^4.9.1", diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index e7bea4f204..c2e7a8855a 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -348,8 +348,8 @@ function record( // When we take a full snapshot, old tracked StyleSheets need to be removed. stylesheetManager.reset(); - // Old shadow doms cache need to be cleared. - shadowDomManager.clearCache(); + + shadowDomManager.init(); mutationBuffers.forEach((buf) => buf.lock()); // don't allow any mirror modifications during snapshotting const node = snapshot(document, { diff --git a/packages/rrweb/src/record/shadow-dom-manager.ts b/packages/rrweb/src/record/shadow-dom-manager.ts index 22034db17b..169c77216a 100644 --- a/packages/rrweb/src/record/shadow-dom-manager.ts +++ b/packages/rrweb/src/record/shadow-dom-manager.ts @@ -26,7 +26,7 @@ export class ShadowDomManager { private scrollCb: scrollCallback; private bypassOptions: BypassOptions; private mirror: Mirror; - private restorePatches: (() => void)[] = []; + private restoreHandlers: (() => void)[] = []; constructor(options: { mutationCb: mutationCallBack; @@ -39,34 +39,20 @@ export class ShadowDomManager { this.bypassOptions = options.bypassOptions; this.mirror = options.mirror; - // Patch 'attachShadow' to observe newly added shadow doms. - // eslint-disable-next-line @typescript-eslint/no-this-alias - const manager = this; - this.restorePatches.push( - patch( - Element.prototype, - 'attachShadow', - function (original: (init: ShadowRootInit) => ShadowRoot) { - return function (this: HTMLElement, option: ShadowRootInit) { - const shadowRoot = original.call(this, option); + this.init(); + } - // For the shadow dom elements in the document, monitor their dom mutations. - // For shadow dom elements that aren't in the document yet, - // we start monitoring them once their shadow dom host is appended to the document. - if (this.shadowRoot && inDom(this)) - manager.addShadowRoot(this.shadowRoot, this.ownerDocument); - return shadowRoot; - }; - }, - ), - ); + public init() { + this.reset(); + // Patch 'attachShadow' to observe newly added shadow doms. + this.patchAttachShadow(Element, document); } public addShadowRoot(shadowRoot: ShadowRoot, doc: Document) { if (!isNativeShadowDom(shadowRoot)) return; if (this.shadowDoms.has(shadowRoot)) return; this.shadowDoms.add(shadowRoot); - initMutationObserver( + const observer = initMutationObserver( { ...this.bypassOptions, doc, @@ -76,14 +62,17 @@ export class ShadowDomManager { }, shadowRoot, ); - initScrollObserver({ - ...this.bypassOptions, - scrollCb: this.scrollCb, - // https://gist.github.com/praveenpuglia/0832da687ed5a5d7a0907046c9ef1813 - // scroll is not allowed to pass the boundary, so we need to listen the shadow document - doc: shadowRoot as unknown as Document, - mirror: this.mirror, - }); + this.restoreHandlers.push(() => observer.disconnect()); + this.restoreHandlers.push( + initScrollObserver({ + ...this.bypassOptions, + scrollCb: this.scrollCb, + // https://gist.github.com/praveenpuglia/0832da687ed5a5d7a0907046c9ef1813 + // scroll is not allowed to pass the boundary, so we need to listen the shadow document + doc: shadowRoot as unknown as Document, + mirror: this.mirror, + }), + ); // Defer this to avoid adoptedStyleSheet events being created before the full snapshot is created or attachShadow action is recorded. setTimeout(() => { if ( @@ -94,12 +83,14 @@ export class ShadowDomManager { shadowRoot.adoptedStyleSheets, this.mirror.getId(shadowRoot.host), ); - initAdoptedStyleSheetObserver( - { - mirror: this.mirror, - stylesheetManager: this.bypassOptions.stylesheetManager, - }, - shadowRoot, + this.restoreHandlers.push( + initAdoptedStyleSheetObserver( + { + mirror: this.mirror, + stylesheetManager: this.bypassOptions.stylesheetManager, + }, + shadowRoot, + ), ); }, 0); } @@ -108,39 +99,57 @@ export class ShadowDomManager { * Monkey patch 'attachShadow' of an IFrameElement to observe newly added shadow doms. */ public observeAttachShadow(iframeElement: HTMLIFrameElement) { - if (iframeElement.contentWindow) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const manager = this; - this.restorePatches.push( - patch( - ( - iframeElement.contentWindow as Window & { - HTMLElement: { prototype: HTMLElement }; - } - ).HTMLElement.prototype, - 'attachShadow', - function (original: (init: ShadowRootInit) => ShadowRoot) { - return function (this: HTMLElement, option: ShadowRootInit) { - const shadowRoot = original.call(this, option); - if (this.shadowRoot) - manager.addShadowRoot( - this.shadowRoot, - iframeElement.contentDocument as Document, - ); - return shadowRoot; - }; - }, - ), - ); - } + if (!iframeElement.contentWindow || !iframeElement.contentDocument) return; + + this.patchAttachShadow( + ( + iframeElement.contentWindow as Window & { + Element: { prototype: Element }; + } + ).Element, + iframeElement.contentDocument, + ); } - public clearCache() { - this.shadowDoms = new WeakSet(); + /** + * Patch 'attachShadow' to observe newly added shadow doms. + */ + private patchAttachShadow( + element: { + prototype: Element; + }, + doc: Document, + ) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const manager = this; + this.restoreHandlers.push( + patch( + element.prototype, + 'attachShadow', + function (original: (init: ShadowRootInit) => ShadowRoot) { + return function (this: Element, option: ShadowRootInit) { + const shadowRoot = original.call(this, option); + // For the shadow dom elements in the document, monitor their dom mutations. + // For shadow dom elements that aren't in the document yet, + // we start monitoring them once their shadow dom host is appended to the document. + if (this.shadowRoot && inDom(this)) + manager.addShadowRoot(this.shadowRoot, doc); + return shadowRoot; + }; + }, + ), + ); } public reset() { - this.restorePatches.forEach((restorePatch) => restorePatch()); - this.clearCache(); + this.restoreHandlers.forEach((handler) => { + try { + handler(); + } catch (e) { + // + } + }); + this.restoreHandlers = []; + this.shadowDoms = new WeakSet(); } } diff --git a/yarn.lock b/yarn.lock index eef747b04e..44271ae283 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2547,11 +2547,6 @@ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz" integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== -"@types/prettier@^2.3.2": - version "2.3.2" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz" - integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog== - "@types/pug@^2.0.4": version "2.0.5" resolved "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz" @@ -10445,12 +10440,7 @@ preserve@^0.2.0: resolved "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== - -prettier@^2.7.1: +prettier@2.8.4, prettier@^2.7.1: version "2.8.4" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== From 227d43abb93d57cadc70c760b28c46911bf7d8ff Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 13 Feb 2023 00:47:51 +1100 Subject: [PATCH 128/201] refine change logs (#1126) --- .changeset/nervous-poets-grin.md | 9 +++++++++ .changeset/real-trains-switch.md | 6 ++++++ .changeset/twenty-lies-switch.md | 9 --------- 3 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 .changeset/nervous-poets-grin.md create mode 100644 .changeset/real-trains-switch.md diff --git a/.changeset/nervous-poets-grin.md b/.changeset/nervous-poets-grin.md new file mode 100644 index 0000000000..eb53dd7f53 --- /dev/null +++ b/.changeset/nervous-poets-grin.md @@ -0,0 +1,9 @@ +--- +'rrdom': patch +'rrdom-nodejs': patch +'rrweb': patch +'rrweb-player': patch +'rrweb-snapshot': patch +--- + +- [`fe69bd6`](https://github.com/rrweb-io/rrweb/commit/fe69bd6456cead304bfc77cf72c9db0f8c030842) [#1087](https://github.com/rrweb-io/rrweb/pull/1087) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs. diff --git a/.changeset/real-trains-switch.md b/.changeset/real-trains-switch.md new file mode 100644 index 0000000000..d102959fdf --- /dev/null +++ b/.changeset/real-trains-switch.md @@ -0,0 +1,6 @@ +--- +'rrdom': patch +'rrweb': patch +--- + +- [`4ee86fe`](https://github.com/rrweb-io/rrweb/commit/4ee86fe66d3e1fe7071f9c8764d82a6fa5c71d57) [#1091](https://github.com/rrweb-io/rrweb/pull/1091) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness. diff --git a/.changeset/twenty-lies-switch.md b/.changeset/twenty-lies-switch.md index 59e52d1e52..de44679861 100644 --- a/.changeset/twenty-lies-switch.md +++ b/.changeset/twenty-lies-switch.md @@ -1,10 +1,5 @@ --- -'rrweb-snapshot': patch -'@rrweb/types': patch -'rrdom': patch -'rrdom-nodejs': patch 'rrweb': patch -'rrweb-player': patch --- Add missing change logs manually. In the next version, all change logs will be generated automatically. @@ -23,8 +18,6 @@ Add missing change logs manually. In the next version, all change logs will be g - [`03821d9`](https://github.com/rrweb-io/rrweb/commit/03821d9b9fa0513e6e373881d43102ceb9388340) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Harmonize on a single getWindowScroll -- [`fe69bd6`](https://github.com/rrweb-io/rrweb/commit/fe69bd6456cead304bfc77cf72c9db0f8c030842) [#1087](https://github.com/rrweb-io/rrweb/pull/1087) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs. - - [`d08913d`](https://github.com/rrweb-io/rrweb/commit/d08913d0dc506dbf119e94686fe5f01c415316c9) [#1086](https://github.com/rrweb-io/rrweb/pull/1086) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: missed adopted style sheets of shadow doms in checkout full snapshot. - [`66abe17`](https://github.com/rrweb-io/rrweb/commit/66abe17832dbb23b3948af1c394f9a02caccc17b) [#1032](https://github.com/rrweb-io/rrweb/pull/1032) Thanks [@dbseel](https://github.com/dbseel)! - Fix: isBlocked throws on invalid HTML element. @@ -47,8 +40,6 @@ Add missing change logs manually. In the next version, all change logs will be g - [`3a26e36`](https://github.com/rrweb-io/rrweb/commit/3a26e36f6f625c0391c7e6d3f1050660adfccc4f) [#1092](https://github.com/rrweb-io/rrweb/pull/1092) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: regression of issue: ShadowHost can't be a string (issue 941) -- [`4ee86fe`](https://github.com/rrweb-io/rrweb/commit/4ee86fe66d3e1fe7071f9c8764d82a6fa5c71d57) [#1091](https://github.com/rrweb-io/rrweb/pull/1091) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness. - - [`07d22e7`](https://github.com/rrweb-io/rrweb/commit/07d22e7cd999a48e7371aaef1b979574bb746500) [#1111](https://github.com/rrweb-io/rrweb/pull/1111) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: enable to customize logger in the replayer. - [`0627d4d`](https://github.com/rrweb-io/rrweb/commit/0627d4df7cc76cde7babbd37ab8e3da5810fb51d) [#1109](https://github.com/rrweb-io/rrweb/pull/1109) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: add option to record on DOMContentLoaded event. From 282c8fa415318e56e8b63fa30ce3f173813b39c3 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 10:15:34 +1100 Subject: [PATCH 129/201] rrweb extension implementation (#1044) * feat: add rrweb web-extension package * refactor: make the extension suitable for manifest v3 * update tsconfig.json * use version_name rather than recorder_version in manifest.json * update manifest.json * enable to keep recording after changing tabs * enable to record between tabs and urls * fix CI error * try to fix CI error * feat: add pause and resume buttons * feat: add a link to new session after recording * improve session list * refactor: migrate session storage from chrome local storage to indexedDB * feat: add pagination to session list * fix: multiple recorders are started after pausing and resuming process * fix: can't stop recording on firefox browser * update type import of 'eventWithTime' * fix CI error * doc: add readme * Apply suggestions from Justin's code review Co-authored-by: Justin Halsall * refactor: make use of webNavigation API to implement recording consistent during page navigation * fix firefox compatibility issue and add title to pages * add mouseleave listener to enhance the recording liability * fix firefox compatibility issue and improve the experience of recording resume after closing tabs * update tsconfig * upgrade vite-plugin-web-extension config to fix some bugs on facebook web page * update import links * refactor: cross tab recording mechanism apply Justin's suggestion * refactor: slipt util/index.ts into multiple files * implement cross-origin iframe recording * fix: regression of issue: ShadowHost can't be a string (issue 941) * refactor shadow dom recording to make tests cover key code * Apply formatting changes * increase the node memory limitation to avoid CI failure * Create lovely-pears-cross.md * Apply formatting changes * Update packages/web-extension/package.json * Update .changeset/lovely-pears-cross.md * update change logs * delete duplicated property --------- Co-authored-by: Justin Halsall --- .changeset/beige-numbers-enjoy.md | 2 - .changeset/lovely-pears-cross.md | 5 + .changeset/nervous-poets-grin.md | 2 +- .changeset/olive-worms-pump.md | 2 - .changeset/real-trains-switch.md | 2 +- .github/workflows/ci-cd.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/style-check.yml | 2 +- .vscode/rrweb-monorepo.code-workspace | 7 +- packages/web-extension/README.md | 32 + packages/web-extension/package.json | 47 + packages/web-extension/src/assets/icon128.png | Bin 0 -> 9279 bytes packages/web-extension/src/assets/icon16.png | Bin 0 -> 3733 bytes packages/web-extension/src/assets/icon48.png | Bin 0 -> 4862 bytes .../web-extension/src/background/index.ts | 162 + .../src/components/CircleButton.tsx | 33 + .../src/components/SidebarWithHeader.tsx | 290 ++ packages/web-extension/src/content/index.ts | 207 + packages/web-extension/src/content/inject.ts | 72 + packages/web-extension/src/manifest.json | 74 + packages/web-extension/src/options/App.tsx | 31 + packages/web-extension/src/options/index.html | 9 + packages/web-extension/src/options/index.tsx | 22 + packages/web-extension/src/pages/App.tsx | 37 + packages/web-extension/src/pages/Player.tsx | 68 + .../web-extension/src/pages/SessionList.tsx | 318 ++ packages/web-extension/src/pages/index.html | 9 + packages/web-extension/src/pages/index.tsx | 22 + packages/web-extension/src/popup/App.tsx | 249 + packages/web-extension/src/popup/Timer.tsx | 25 + packages/web-extension/src/popup/index.tsx | 15 + packages/web-extension/src/popup/popup.html | 8 + packages/web-extension/src/types.ts | 86 + packages/web-extension/src/utils/channel.ts | 180 + packages/web-extension/src/utils/index.ts | 44 + packages/web-extension/src/utils/recording.ts | 90 + packages/web-extension/src/utils/storage.ts | 90 + packages/web-extension/tsconfig.json | 41 + packages/web-extension/vite.config.ts | 106 + yarn.lock | 4065 +++++++++++++++-- 40 files changed, 5950 insertions(+), 508 deletions(-) delete mode 100644 .changeset/beige-numbers-enjoy.md create mode 100644 .changeset/lovely-pears-cross.md delete mode 100644 .changeset/olive-worms-pump.md create mode 100644 packages/web-extension/README.md create mode 100644 packages/web-extension/package.json create mode 100644 packages/web-extension/src/assets/icon128.png create mode 100644 packages/web-extension/src/assets/icon16.png create mode 100644 packages/web-extension/src/assets/icon48.png create mode 100644 packages/web-extension/src/background/index.ts create mode 100644 packages/web-extension/src/components/CircleButton.tsx create mode 100644 packages/web-extension/src/components/SidebarWithHeader.tsx create mode 100644 packages/web-extension/src/content/index.ts create mode 100644 packages/web-extension/src/content/inject.ts create mode 100644 packages/web-extension/src/manifest.json create mode 100644 packages/web-extension/src/options/App.tsx create mode 100644 packages/web-extension/src/options/index.html create mode 100644 packages/web-extension/src/options/index.tsx create mode 100644 packages/web-extension/src/pages/App.tsx create mode 100644 packages/web-extension/src/pages/Player.tsx create mode 100644 packages/web-extension/src/pages/SessionList.tsx create mode 100644 packages/web-extension/src/pages/index.html create mode 100644 packages/web-extension/src/pages/index.tsx create mode 100644 packages/web-extension/src/popup/App.tsx create mode 100644 packages/web-extension/src/popup/Timer.tsx create mode 100644 packages/web-extension/src/popup/index.tsx create mode 100644 packages/web-extension/src/popup/popup.html create mode 100644 packages/web-extension/src/types.ts create mode 100644 packages/web-extension/src/utils/channel.ts create mode 100644 packages/web-extension/src/utils/index.ts create mode 100644 packages/web-extension/src/utils/recording.ts create mode 100644 packages/web-extension/src/utils/storage.ts create mode 100644 packages/web-extension/tsconfig.json create mode 100644 packages/web-extension/vite.config.ts diff --git a/.changeset/beige-numbers-enjoy.md b/.changeset/beige-numbers-enjoy.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/beige-numbers-enjoy.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/lovely-pears-cross.md b/.changeset/lovely-pears-cross.md new file mode 100644 index 0000000000..edf88c50fd --- /dev/null +++ b/.changeset/lovely-pears-cross.md @@ -0,0 +1,5 @@ +--- +'@rrweb/web-extension': patch +--- + +Add rrweb browser extension diff --git a/.changeset/nervous-poets-grin.md b/.changeset/nervous-poets-grin.md index eb53dd7f53..084e1c5524 100644 --- a/.changeset/nervous-poets-grin.md +++ b/.changeset/nervous-poets-grin.md @@ -6,4 +6,4 @@ 'rrweb-snapshot': patch --- -- [`fe69bd6`](https://github.com/rrweb-io/rrweb/commit/fe69bd6456cead304bfc77cf72c9db0f8c030842) [#1087](https://github.com/rrweb-io/rrweb/pull/1087) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs. +Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). diff --git a/.changeset/olive-worms-pump.md b/.changeset/olive-worms-pump.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/olive-worms-pump.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/real-trains-switch.md b/.changeset/real-trains-switch.md index d102959fdf..71de446836 100644 --- a/.changeset/real-trains-switch.md +++ b/.changeset/real-trains-switch.md @@ -3,4 +3,4 @@ 'rrweb': patch --- -- [`4ee86fe`](https://github.com/rrweb-io/rrweb/commit/4ee86fe66d3e1fe7071f9c8764d82a6fa5c71d57) [#1091](https://github.com/rrweb-io/rrweb/pull/1091) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness. +Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b94c259fcb..a1ad8e81cf 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -24,7 +24,7 @@ jobs: run: yarn - name: Build Project - run: yarn build:all + run: NODE_OPTIONS='--max-old-space-size=4096' yarn build:all - name: Check types run: yarn turbo run check-types diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5f4afccce..0f9688d18a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: id: changesets uses: changesets/action@v1 with: - publish: yarn run release + publish: NODE_OPTIONS='--max-old-space-size=4096' yarn run release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index 50498ee960..79320836dc 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -20,7 +20,7 @@ jobs: - name: Install Dependencies run: yarn - name: Build Packages - run: yarn build:all + run: NODE_OPTIONS='--max-old-space-size=4096' yarn build:all - name: Eslint Check run: yarn turbo run lint - name: Save Code Linting Report JSON diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 6a1be41ead..43fd69fb85 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -25,9 +25,10 @@ "path": "../packages/rrweb-snapshot" }, { - "name": "@rrweb/types", - "path": "../packages/types" - } + "name": "web-extension (package)", + "path": "../packages/web-extension" + }, + { "name": "@rrweb/types", "path": "../packages/types" } ], "settings": { "jest.disabledWorkspaceFolders": [ diff --git a/packages/web-extension/README.md b/packages/web-extension/README.md new file mode 100644 index 0000000000..5acb7b6cc9 --- /dev/null +++ b/packages/web-extension/README.md @@ -0,0 +1,32 @@ +

+ +

+ +# rrweb extension + +The package web-extension provides a browser extension for recording and replaying web pages. + +## Installation + +``` +yarn install +``` + +## Build + +```bash +# build for chrome +yarn build:chrome + +# build for firefox +yarn build:firefox +``` + +## Development + +```bash +# start a development chrome browser +yarn dev:chrome +# start a development firefox browser +yarn dev:firefox +``` diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json new file mode 100644 index 0000000000..54679add8a --- /dev/null +++ b/packages/web-extension/package.json @@ -0,0 +1,47 @@ +{ + "name": "@rrweb/web-extension", + "private": true, + "version": "2.0.0", + "description": "The web extension of rrweb which helps to run rrweb on any website out of box", + "author": "rrweb-io", + "license": "MIT", + "scripts": { + "dev:chrome": "cross-env TARGET_BROWSER=chrome vite dev", + "dev:firefox": "cross-env TARGET_BROWSER=firefox vite dev", + "build:chrome": "cross-env TARGET_BROWSER=chrome vite build", + "build:firefox": "cross-env TARGET_BROWSER=firefox vite build", + "pack:chrome": "cross-env TARGET_BROWSER=chrome ZIP=true vite build", + "pack:firefox": "cross-env TARGET_BROWSER=firefox ZIP=true vite build", + "check-types": "tsc -noEmit", + "prepublish": "npm run pack:chrome && npm run pack:firefox" + }, + "devDependencies": { + "@rrweb/types": "^2.0.0-alpha.4", + "@types/react-dom": "^18.0.6", + "@types/webextension-polyfill": "^0.9.1", + "@vitejs/plugin-react": "^2.1.0", + "cross-env": "^7.0.3", + "type-fest": "^2.19.0", + "typescript": "^4.7.3", + "vite": "^3.1.8", + "vite-plugin-web-extension": "^1.4.5", + "vite-plugin-zip": "^1.0.1", + "webextension-polyfill": "^0.10.0" + }, + "dependencies": { + "@chakra-ui/react": "^2.3.4", + "@emotion/react": "^11.10.4", + "@emotion/styled": "^11.10.4", + "@tanstack/react-table": "^8.5.22", + "framer-motion": "^7.3.6", + "idb": "^7.1.1", + "mitt": "^3.0.0", + "nanoid": "^4.0.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-icons": "^4.4.0", + "react-router-dom": "^6.4.1", + "rrweb": "^2.0.0-alpha.4", + "rrweb-player": "^1.0.0-alpha.4" + } +} diff --git a/packages/web-extension/src/assets/icon128.png b/packages/web-extension/src/assets/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..c053fb110dcffdd2fffc8688ec5b23f728a99e54 GIT binary patch literal 9279 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 literal 0 HcmV?d00001 diff --git a/packages/web-extension/src/assets/icon16.png b/packages/web-extension/src/assets/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..26a6bdd0185253e58d398f7b4293e31596b564b3 GIT binary patch literal 3733 zcmV;G4r=j4Tx07!|IR|i;A$rhelQX#Ye0#ZV6f=CS@C4?40dJ#k=BmqK+F%U%TWl_Pk ziv?`xs)%Bz*%3R7VDF0s1!Y|<*p)XapzFT!z4^Ym^WT5Y{O8QMGxy#BK>Z|FEY3wW z0LT*+NMb_*7-N%D7^J6w3=9DkI0APrFJH`xj*bAZ47PtS?`{A@)?RgxY5lYRzZOlt zAfE>S5)E-WKcANe@gV^4N}gC!002?On@=ea%P_1!TLJ@wc%ckuDsZI?rz`M2xlMd5 z8{!rK$f{hfBohF&cE~ddd6`f{tq)=+5nm_*K%D`xZ(iOcKE!Dd+dvyJ#4>$m`VTsp zf747K(&Tb8hp=KU@Y7Bs5kwXPCEJIg`R=SQX-4 zQF>$)#99!45c1{r5Tn#AX=sAN9yR3UvtiDFF2qiJZeTdXh7bpfaw8*@e0qj3m;*6f zA!w1XfD;d~9>fa;`9X0?o6V9*u}WWPU513sQu61y61iWQuLshc1eQ|&b(Vmm)W_6{ zv*ME=rb28{C`^omSPf#w{G7OOrOoK#EOz9ejYJwNi{Ap`Sb-=cK%tKnW=Mi#mG-51 z`GfPtHfISrkxG1^AS*spksqvu$Cbwq=Yq8hM685C9YOxsh{3t=1%W||xntb|Q9_(j z=YzN)AXYIJr;2lCJ+DzB{Mi8N!}xZ*EmoN{)9*X@`uI+ z$a@8#5b|G#SjE)=WXO99KB$w&u1GSmkxi9y6bVkj|~Xp0-*zPL9Y0k8hBI^#^-b8sHt?Fml_9fU`O z>x53i?TNymT!@h&6pE-|V5DpZ&9c$UYyo z;Xhkq3$cUPIqVd60XvG-0|s^iYrtBuV=_Fnx}L~a*Pyppc|18#NB9qagHKkL$$Y08 zryi#-ioOCtk?b7}*ptLlCBn?C0tO3y5d;j5i03$h;q2t>41nzWqIiA1iIIOd5Z%`A z+`K;k@HsIsFmU8M7j^=G{T^`pH-6`AnDEn~0>Jr3o>Wq(;BeW60N)boaHr`5V_*(! z;C!5cJMaenAP9tkC=d^l!2}q67F_2NFa=Bpb3h3w1Ixf_upVpzTft7S2OI$P;3zl+ z&Voyz9b5-@;p*uEFF+r72RdVvIVI|_9KnRDdYmuf!szOB0b1!(Eo^CGSkybu3Ipb-oS_5?2i zhcK3qNti+?A(Rug6Aly3z`fK(ct<1>b&1wQ54abTiP^+y#Kpu7#J$9m#17(PVn2yQ z(kI!Id`M9wE~$W2LRv@KO*%okLh2%YAgho~$j;o4m9mtwjnYK9OzEP0rfO2HsXo+rYBqHabuG1)dWL$B`bI@X#Z1LhB}PT4 zGFxS>%089zDi2jYs%oj)sj^j5Ri&y+RClPJRK2bGhDM`V(fnyCv;x`^S~cx7?E&qB z8ePp%jibg_o26EvR;PAF?WHqrMd@nZ|Hs+W;Bd7OfYQG zu-ai)hP~GtuIH~O&@0v3t9M=RlRiT~NIyq^xqiL=eFNOU&LGBMior&MGX}lGwTF8T z=M66%eqi_=L)6gDFxGII;a0;|!+s-UqYxvp(K@5kM!m+m#{S0H#;c5v8~2#dO?*v+ zCaX+Nm^^3bFj$OS##%-TqtDdPG}KgTy2Z53^s||@S%TSIv%O~b&8g;0^9=J<=FR4P z7A6*BET&u3SlqFsSTZfMEZ10`vwUx5ZIxuTz^dM=+gi_>V_j@rV|~wtX5(iw*`~_o znk~_mX`5ra!M5EFwR5w}va7JWY>(Qz+Y9YC*msN|jPM*G8c{Xkrh}>j%c0Pr#^Ir( zu49DbT*rFHmm@7kri@%Z^4!R8PHs-QPTQRBj?x+xHfrvurctk*?VNee70%aOXfDAn zvs@ZnUb))43S2k3-g2Y6jd5Gx*6jAx-NRktUhCfDVd0VHQR#7$sm+XLE@qzhBzOjR z&hk9!`O(YWtHA4k*I(Wa-Z|d8yt_wRj~0yHHu|xTnGe@zi%+L7!#B-$vu~%LsUO#` z%I}fCxj)~3yZyVG7t+42`mk43(^Wo2wETXAlNK8 zGq@(WFT^cmTFA*zLTGsC^3aeVrpV_;`^k)q!me>V@HmiG4@ijL9#HpA%&EZn6f>ke_YVG)#JKS-BL?Z zZ;ZDdUo`&01cM2=6OK((pU9nfFpZFwl(sAF3pa{e#qCe$q*tc*@z}gIycc|b{wjWt zz(=q`@GQe8V?{=HrcdU|%$_X2tn#d0AzQd!_$oUzdsFuNoT!`~IRm*#xqI`-d1-l# zA}vvlsAZDzq@qb3Vta9kxO1}C*t8H|C|uaK$Ys&G zMc+#Kr5DSb%F4^WEaok~`0J=&*Zw-NBx6b2Qun1Bml2nVmfcwHw|wUc%@xHf9Ys_ho(7`s)<|6?-cUD;HJ1-@xB+ zWuyPb+D%5A$~Jx4EZlr+OURbGDyypUt;DT`Tc2)A+;;9a=5N*84YrqU|GGo8qjP8M z&X#JA>grvFyO!3VHPV`%-Ko3V_XO@~sCB5_vR8L+**>sOy6^e^wEZ^@gdb==$UInk z$nsF-Vfx|HI;3uDU0;1xeP=^*Lq}s+V{?;t)8QkIM|K=FJz8;0=h)KY)Z_Dy51c4I z(SK5WviDTxsmIL|o9~_;d-_^SOiSCD@H6Mn2A@5Bj&<(Dd7tw~FL+&OyvV#*f64t) zU8`H`;oseUKiuZtR(ILsazndkdsD~gj^kJSuAI6Wc=hZx&b3R|Bd>SdNVsw9X6nt( zTl`zyw{vg5x>I=P^WE9^2=_|wYuqn?VECZw58FTXcDi>Sdl>kz^-xKBm=iZV(HUF%9Y4h^XU;cl!_Kof9dL?-^ z@OtqZ{Wm+_y1s4hkM4i)F7Msv_oW~7KkWR-{CMtD(x+#ir+guOS^L%YYty&zZ}$fB z1_s1j30M9rfWeApWB~Ab2LQBW0O;_1MjfX(|H%uYIJ3$T_)l1IMwAzK0BYg?drU9< zySWL#zHRXM1@+Za0f?pn5bp_47nh+rcvh32D`kZU&jFhwzozG0BmJgNoGw=04e|g00;mC0U7`W000010000!0UH1X00001 z0000+0Vn_i0000100IC2hiL!=000010000^000000000;000010000;0000100N)_ z00aO40096101%)900aO40096101yBG002j3r40Z8010qNS#tmY3ljhU3ljkVnw%H_ z00D(bL_t(Y4ZYN{OT$1E$MJ8~S<*p7M2!e;V%!A5c65uVlMdCtK-AgUMFeqka1z8# zaC6d0#3cd2L6AC$TdRT~h0|Y42sRuSq%z033ax;>1+!&=Nl)58hmR(Bs0wU^0++GC?S*8&OCrt| zESCiCtCAgGYXUo=!11BL%Csb9?4z><4Fnz|vH$2T5?ELl_-?Z2CvTiPkR-ZBFOak) zy@>mA?m+U3VPBFN%m)G!fxw^}kkdR1VzB5+j`xQhgy>H@1JfrChF zWz%@>9r#4xEEZVu<##7a0{dG6aZSQ(zx>|wyd~g;eWTFQ!DrZyEy1s)YD%<00000NkvXXu0mjflqm7v literal 0 HcmV?d00001 diff --git a/packages/web-extension/src/assets/icon48.png b/packages/web-extension/src/assets/icon48.png new file mode 100644 index 0000000000000000000000000000000000000000..31d04fa38a0d0443345960fe9d802cdacc410c20 GIT binary patch literal 4862 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 { + // assign default value to settings of this extension + const result = + ((await Browser.storage.sync.get(SyncDataKey.settings)) as SyncData) || + undefined; + const defaultSettings: Settings = {}; + let settings = defaultSettings; + if (result && result.settings) { + setDefaultSettings(result.settings, defaultSettings); + settings = result.settings; + } + await Browser.storage.sync.set({ + settings, + } as SyncData); + + // When tab is changed during the recording process, pause recording in the old tab and start a new one in the new tab. + Browser.tabs.onActivated.addListener((activeInfo) => { + Browser.storage.local + .get(LocalDataKey.recorderStatus) + .then(async (data) => { + const localData = data as LocalData; + if (!localData || !localData[LocalDataKey.recorderStatus]) return; + let statusData = localData[LocalDataKey.recorderStatus]; + let { status } = statusData; + let bufferedEvents: eventWithTime[] | undefined; + + if (status === RecorderStatus.RECORDING) { + const result = await pauseRecording( + channel, + RecorderStatus.PausedSwitch, + statusData, + ).catch(async () => { + /** + * This error happen when the old tab is closed. + * In this case, the recording process would be stopped through Browser.tabs.onRemoved API. + * So we just read the new status here. + */ + const localData = (await Browser.storage.local.get( + LocalDataKey.recorderStatus, + )) as LocalData; + return { + status: localData[LocalDataKey.recorderStatus], + bufferedEvents, + }; + }); + if (!result) return; + statusData = result.status; + status = statusData.status; + bufferedEvents = result.bufferedEvents; + } + if (status === RecorderStatus.PausedSwitch) + await resumeRecording( + channel, + activeInfo.tabId, + statusData, + bufferedEvents, + ); + }) + .catch(() => { + // the extension can't access to the tab + }); + }); + + // If the recording can't start on an invalid tab, resume it when the tab content is updated. + Browser.tabs.onUpdated.addListener(function (tabId, info) { + if (info.status !== 'complete') return; + Browser.storage.local + .get(LocalDataKey.recorderStatus) + .then(async (data) => { + const localData = data as LocalData; + if (!localData || !localData[LocalDataKey.recorderStatus]) return; + const { status, activeTabId } = localData[LocalDataKey.recorderStatus]; + if (status !== RecorderStatus.PausedSwitch || activeTabId === tabId) + return; + await resumeRecording( + channel, + tabId, + localData[LocalDataKey.recorderStatus], + ); + }) + .catch(() => { + // the extension can't access to the tab + }); + }); + + /** + * When the current tab is closed, the recording events will be lost because this event is fired after it is closed. + * This event listener is just used to make sure the recording status is updated. + */ + Browser.tabs.onRemoved.addListener((tabId) => { + Browser.storage.local + .get(LocalDataKey.recorderStatus) + .then(async (data) => { + const localData = data as LocalData; + if (!localData || !localData[LocalDataKey.recorderStatus]) return; + const { status, activeTabId, startTimestamp } = + localData[LocalDataKey.recorderStatus]; + if (activeTabId !== tabId || status !== RecorderStatus.RECORDING) + return; + + // Update the recording status to make it resumable after users switch to other tabs. + const statusData: LocalData[LocalDataKey.recorderStatus] = { + status: RecorderStatus.PausedSwitch, + activeTabId, + startTimestamp, + pausedTimestamp: Date.now(), + }; + await Browser.storage.local.set({ + [LocalDataKey.recorderStatus]: statusData, + }); + }) + .catch((err) => { + console.error(err); + }); + }); +})(); + +/** + * Update existed settings with new settings. + * Set new setting values if these properties don't exist in older versions. + */ +function setDefaultSettings( + existedSettings: Record, + newSettings: Record, +) { + for (const i in newSettings) { + // settings[i] contains key-value settings + if ( + typeof newSettings[i] === 'object' && + !(newSettings[i] instanceof Array) && + Object.keys(newSettings[i] as Record).length > 0 + ) { + if (existedSettings[i]) { + setDefaultSettings( + existedSettings[i] as Record, + newSettings[i] as Record, + ); + } else { + // settings[i] contains several setting items but these have not been set before + existedSettings[i] = newSettings[i]; + } + } else if (existedSettings[i] === undefined) { + // settings[i] is a single setting item and it has not been set before + existedSettings[i] = newSettings[i]; + } + } +} diff --git a/packages/web-extension/src/components/CircleButton.tsx b/packages/web-extension/src/components/CircleButton.tsx new file mode 100644 index 0000000000..abbb68e576 --- /dev/null +++ b/packages/web-extension/src/components/CircleButton.tsx @@ -0,0 +1,33 @@ +import { Button, ButtonProps } from '@chakra-ui/react'; + +interface CircleButtonProps extends ButtonProps { + diameter: number; + onClick?: () => void; + children?: React.ReactNode; + title?: string; +} + +export function CircleButton({ + diameter, + onClick, + children, + title, + ...rest +}: CircleButtonProps) { + return ( + + ); +} diff --git a/packages/web-extension/src/components/SidebarWithHeader.tsx b/packages/web-extension/src/components/SidebarWithHeader.tsx new file mode 100644 index 0000000000..ba8470a5b6 --- /dev/null +++ b/packages/web-extension/src/components/SidebarWithHeader.tsx @@ -0,0 +1,290 @@ +import { ReactNode } from 'react'; +import { + IconButton, + Box, + CloseButton, + Flex, + HStack, + Icon, + Image, + useColorModeValue, + Link, + Drawer, + DrawerContent, + useDisclosure, + BoxProps, + FlexProps, + Heading, + Stack, + Text, + Popover, + PopoverTrigger, + PopoverContent, +} from '@chakra-ui/react'; +import { FiChevronRight, FiMenu } from 'react-icons/fi'; +import type { IconType } from 'react-icons'; +import Browser from 'webextension-polyfill'; + +export interface SideBarItem { + label: string; + icon: IconType; + href: string; +} + +export interface HeadBarItem { + label: string; + subLabel?: string; + children?: Array; + href?: string; +} + +export default function SidebarWithHeader({ + children, + title, + headBarItems, + sideBarItems, +}: { + title?: string; + sideBarItems: SideBarItem[]; + headBarItems: SideBarItem[]; + children: ReactNode; +}) { + const { isOpen, onOpen, onClose } = useDisclosure(); + return ( + + onClose} + display={{ base: 'none', md: 'block' }} + title={title} + /> + + + + + + + + + + + {children} + + ); +} + +interface SidebarProps extends BoxProps { + onClose: () => void; + title?: string; + sideBarItems: SideBarItem[]; +} + +const SidebarContent = ({ + onClose, + sideBarItems, + title, + ...rest +}: SidebarProps) => { + return ( + + + + + + {title && ( + + {title} + + )} + + + {sideBarItems.map((link) => ( + + {link.label} + + ))} + + ); +}; + +interface NavItemProps extends FlexProps { + icon: IconType; + href: string; + children: string; +} +const NavItem = ({ icon, href, children, ...rest }: NavItemProps) => { + return ( + + + <> + {icon && } + {children} + + + + ); +}; + +interface MobileProps extends FlexProps { + onOpen: () => void; +} +const MobileNav = ({ onOpen, ...rest }: MobileProps) => { + return ( + + } + /> + + + {rest.children && rest.children} + + + ); +}; + +const DesktopNav = ({ headBarItems }: { headBarItems: HeadBarItem[] }) => { + const linkColor = useColorModeValue('gray.600', 'gray.200'); + const linkHoverColor = useColorModeValue('gray.800', 'white'); + const popoverContentBgColor = useColorModeValue('white', 'gray.800'); + + return ( + + {headBarItems.map((navItem) => ( + + + + + {navItem.label} + + + + {navItem.children && ( + + + {navItem.children.map((child) => ( + + ))} + + + )} + + + ))} + + ); +}; + +const DesktopSubNav = ({ label, href, subLabel }: HeadBarItem) => { + return ( + + + + + {label} + + {subLabel} + + + + + + + ); +}; diff --git a/packages/web-extension/src/content/index.ts b/packages/web-extension/src/content/index.ts new file mode 100644 index 0000000000..0fec24b154 --- /dev/null +++ b/packages/web-extension/src/content/index.ts @@ -0,0 +1,207 @@ +import Browser, { Storage } from 'webextension-polyfill'; +import { nanoid } from 'nanoid'; +import type { eventWithTime } from '@rrweb/types'; +import { + LocalData, + LocalDataKey, + RecorderStatus, + ServiceName, + Session, + RecordStartedMessage, + RecordStoppedMessage, + MessageName, + EmitEventMessage, +} from '~/types'; +import Channel from '~/utils/channel'; +import { isInCrossOriginIFrame } from '~/utils'; + +const channel = new Channel(); + +void (() => { + window.addEventListener( + 'message', + ( + event: MessageEvent<{ + message: MessageName; + }>, + ) => { + if (event.source !== window) return; + if (event.data.message === MessageName.RecordScriptReady) + window.postMessage( + { + message: MessageName.StartRecord, + config: { + recordCrossOriginIframes: true, + }, + }, + location.origin, + ); + }, + ); + if (isInCrossOriginIFrame()) { + void initCrossOriginIframe(); + } else { + void initMainPage(); + } +})(); + +async function initMainPage() { + let bufferedEvents: eventWithTime[] = []; + let newEvents: eventWithTime[] = []; + let startResponseCb: ((response: RecordStartedMessage) => void) | undefined = + undefined; + channel.provide(ServiceName.StartRecord, async () => { + startRecord(); + return new Promise((resolve) => { + startResponseCb = (response) => { + resolve(response); + }; + }); + }); + channel.provide(ServiceName.ResumeRecord, async (params) => { + const { events, pausedTimestamp } = params as { + events: eventWithTime[]; + pausedTimestamp: number; + }; + bufferedEvents = events; + startRecord(); + return new Promise((resolve) => { + startResponseCb = (response) => { + const pausedTime = response.startTimestamp - pausedTimestamp; + // Decrease the time spent in the pause state and make them look like a continuous recording. + bufferedEvents.forEach((event) => { + event.timestamp += pausedTime; + }); + resolve(response); + }; + }); + }); + let stopResponseCb: ((response: RecordStoppedMessage) => void) | undefined = + undefined; + channel.provide(ServiceName.StopRecord, () => { + window.postMessage({ message: MessageName.StopRecord }); + return new Promise((resolve) => { + stopResponseCb = (response: RecordStoppedMessage) => { + stopResponseCb = undefined; + const newSession = generateSession(); + response.session = newSession; + bufferedEvents = []; + newEvents = []; + resolve(response); + // clear cache + void Browser.storage.local.set({ + [LocalDataKey.bufferedEvents]: [], + }); + }; + }); + }); + channel.provide(ServiceName.PauseRecord, () => { + window.postMessage({ message: MessageName.StopRecord }); + return new Promise((resolve) => { + stopResponseCb = (response: RecordStoppedMessage) => { + stopResponseCb = undefined; + bufferedEvents = []; + newEvents = []; + resolve(response); + void Browser.storage.local.set({ + [LocalDataKey.bufferedEvents]: response.events, + }); + }; + }); + }); + + window.addEventListener( + 'message', + ( + event: MessageEvent< + | RecordStartedMessage + | RecordStoppedMessage + | EmitEventMessage + | { + message: MessageName; + } + >, + ) => { + if (event.source !== window) return; + else if ( + event.data.message === MessageName.RecordStarted && + startResponseCb + ) + startResponseCb(event.data as RecordStartedMessage); + else if ( + event.data.message === MessageName.RecordStopped && + stopResponseCb + ) { + const data = event.data as RecordStoppedMessage; + // On firefox, the event.data is immutable, so we need to clone it to avoid errors. + const newData = { + ...data, + }; + newData.events = bufferedEvents.concat(data.events); + stopResponseCb(newData); + } else if (event.data.message === MessageName.EmitEvent) + newEvents.push((event.data as EmitEventMessage).event); + }, + ); + + const localData = (await Browser.storage.local.get()) as LocalData; + if ( + localData?.[LocalDataKey.recorderStatus]?.status === + RecorderStatus.RECORDING + ) { + startRecord(); + bufferedEvents = localData[LocalDataKey.bufferedEvents] || []; + } + + // Before unload pages, cache the new events in the local storage. + window.addEventListener('beforeunload', () => { + void Browser.storage.local.set({ + [LocalDataKey.bufferedEvents]: bufferedEvents.concat(newEvents), + }); + }); +} + +async function initCrossOriginIframe() { + Browser.storage.local.onChanged.addListener((change) => { + if (change[LocalDataKey.recorderStatus]) { + const statusChange = change[ + LocalDataKey.recorderStatus + ] as Storage.StorageChange; + const newStatus = + statusChange.newValue as LocalData[LocalDataKey.recorderStatus]; + if (newStatus.status === RecorderStatus.RECORDING) startRecord(); + else + window.postMessage( + { message: MessageName.StopRecord }, + location.origin, + ); + } + }); + const localData = (await Browser.storage.local.get()) as LocalData; + if ( + localData?.[LocalDataKey.recorderStatus]?.status === + RecorderStatus.RECORDING + ) + startRecord(); +} + +function startRecord() { + const scriptEl = document.createElement('script'); + scriptEl.src = Browser.runtime.getURL('content/inject.js'); + document.documentElement.appendChild(scriptEl); + scriptEl.onload = () => { + document.documentElement.removeChild(scriptEl); + }; +} + +function generateSession() { + const newSession: Session = { + id: nanoid(), + name: document.title, + tags: [], + createTimestamp: Date.now(), + modifyTimestamp: Date.now(), + recorderVersion: Browser.runtime.getManifest().version_name || 'unknown', + }; + return newSession; +} diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts new file mode 100644 index 0000000000..0f2d7d3f67 --- /dev/null +++ b/packages/web-extension/src/content/inject.ts @@ -0,0 +1,72 @@ +import { record } from 'rrweb'; +import type { recordOptions } from 'rrweb/typings/types'; +import type { eventWithTime } from '@rrweb/types'; +import { MessageName, RecordStartedMessage } from '~/types'; +import { isInCrossOriginIFrame } from '~/utils'; + +/** + * This script is injected into both main page and cross-origin IFrames through + diff --git a/packages/web-extension/src/options/index.tsx b/packages/web-extension/src/options/index.tsx new file mode 100644 index 0000000000..0114523911 --- /dev/null +++ b/packages/web-extension/src/options/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ChakraProvider } from '@chakra-ui/react'; +import * as ReactDOM from 'react-dom/client'; +import { createHashRouter, RouterProvider } from 'react-router-dom'; +import App from './App'; + +const rootElement = document.getElementById('root'); +const router = createHashRouter([ + { + path: '/*', + element: , + }, +]); + +rootElement && + ReactDOM.createRoot(rootElement).render( + + + + + , + ); diff --git a/packages/web-extension/src/pages/App.tsx b/packages/web-extension/src/pages/App.tsx new file mode 100644 index 0000000000..9a352a95a4 --- /dev/null +++ b/packages/web-extension/src/pages/App.tsx @@ -0,0 +1,37 @@ +import { Route, Routes } from 'react-router-dom'; +import SidebarWithHeader from '~/components/SidebarWithHeader'; +import { SessionList } from './SessionList'; +import { FiList, FiSettings } from 'react-icons/fi'; +import Player from './Player'; + +export default function App() { + return ( + + + } /> + } /> + + + ); +} diff --git a/packages/web-extension/src/pages/Player.tsx b/packages/web-extension/src/pages/Player.tsx new file mode 100644 index 0000000000..b9744a8fa4 --- /dev/null +++ b/packages/web-extension/src/pages/Player.tsx @@ -0,0 +1,68 @@ +import { useRef, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import Replayer from 'rrweb-player'; +import { + Box, + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + Center, +} from '@chakra-ui/react'; +import { getEvents, getSession } from '~/utils/storage'; + +export default function Player() { + const playerElRef = useRef(null); + const playerRef = useRef(null); + const { sessionId } = useParams(); + const [sessionName, setSessionName] = useState(''); + + useEffect(() => { + if (!sessionId) return; + getSession(sessionId) + .then((session) => { + setSessionName(session.name); + }) + .catch((err) => { + console.error(err); + }); + getEvents(sessionId) + .then((events) => { + if (!playerElRef.current || !sessionId) return; + + const linkEl = document.createElement('link'); + linkEl.href = + 'https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css'; + linkEl.rel = 'stylesheet'; + document.head.appendChild(linkEl); + playerRef.current = new Replayer({ + target: playerElRef.current as HTMLElement, + props: { + events, + autoPlay: true, + }, + }); + }) + .catch((err) => { + console.error(err); + }); + return () => { + playerRef.current?.pause(); + }; + }, [sessionId]); + + return ( + <> + + + Sessions + + + {sessionName} + + +
+ +
+ + ); +} diff --git a/packages/web-extension/src/pages/SessionList.tsx b/packages/web-extension/src/pages/SessionList.tsx new file mode 100644 index 0000000000..8aead1834f --- /dev/null +++ b/packages/web-extension/src/pages/SessionList.tsx @@ -0,0 +1,318 @@ +import { useEffect, useMemo, useState } from 'react'; +import { + chakra, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + Text, + TableContainer, + Flex, + Checkbox, + Button, + Spacer, + IconButton, + Select, + Input, + Divider, +} from '@chakra-ui/react'; +import { + createColumnHelper, + useReactTable, + flexRender, + getCoreRowModel, + SortingState, + getSortedRowModel, + PaginationState, +} from '@tanstack/react-table'; +import { VscTriangleDown, VscTriangleUp } from 'react-icons/vsc'; +import { useNavigate } from 'react-router-dom'; +import { Session, EventName } from '~/types'; +import Channel from '~/utils/channel'; +import { deleteSessions, getAllSessions } from '~/utils/storage'; +import { + FiChevronLeft, + FiChevronRight, + FiChevronsLeft, + FiChevronsRight, +} from 'react-icons/fi'; + +const columnHelper = createColumnHelper(); +const channel = new Channel(); + +export function SessionList() { + const [sessions, setSessions] = useState([]); + const navigate = useNavigate(); + const [sorting, setSorting] = useState([ + { + id: 'createTimestamp', + desc: true, + }, + ]); + const [rowSelection, setRowSelection] = useState({}); + + const [{ pageIndex, pageSize }, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const fetchDataOptions = { + pageIndex, + pageSize, + }; + + const fetchData = (options: { pageIndex: number; pageSize: number }) => { + return { + rows: sessions.slice( + options.pageIndex * options.pageSize, + (options.pageIndex + 1) * options.pageSize, + ), + pageCount: Math.ceil(sessions.length / options.pageSize), + }; + }; + const pagination = useMemo( + () => ({ + pageIndex, + pageSize, + }), + [pageIndex, pageSize], + ); + + const columns = useMemo( + () => [ + columnHelper.display({ + id: 'select', + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }), + columnHelper.accessor((row) => row.name, { + cell: (info) => info.getValue(), + header: 'Name', + }), + columnHelper.accessor((row) => row.createTimestamp, { + id: 'createTimestamp', + cell: (info) => new Date(info.getValue()).toLocaleString(), + header: 'Created Time', + sortDescFirst: true, + }), + columnHelper.accessor((row) => row.recorderVersion, { + cell: (info) => info.getValue(), + header: 'RRWEB Version', + }), + ], + [], + ); + const table = useReactTable({ + columns, + data: fetchData(fetchDataOptions).rows, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setRowSelection, + onSortingChange: setSorting, + getSortedRowModel: getSortedRowModel(), + state: { + pagination, + sorting, + rowSelection, + }, + manualPagination: true, + pageCount: fetchData(fetchDataOptions).pageCount, + }); + + const updateSessions = async () => { + const sessions = await getAllSessions(); + setSessions(sessions); + }; + + useEffect(() => { + void updateSessions(); + channel.on(EventName.SessionUpdated, () => { + void updateSessions(); + }); + }, []); + + return ( + <> + +
- + -
Mark-Fenng +
Yun Feng
@@ -141,7 +141,7 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE - The first ever UX automation tool + The first ever UX automation tool
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + const meta = header.column.columnDef.meta as + | { + isNumeric: boolean; + } + | undefined; + return ( + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell, index) => { + const meta = cell.column.columnDef.meta as + | { + isNumeric: boolean; + } + | undefined; + return ( + + ); + })} + + ))} + +
+ + {flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + {{ + asc: ( + + ), + desc: ( + + ), + }[header.column.getIsSorted() as string] ?? null} + + +
{ + if (index !== 0) + navigate(`/session/${row.original.id}`); + }} + > + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} +
+ + + + + table.setPageIndex(0)} + disabled={!table.getCanPreviousPage()} + > + + + table.previousPage()} + disabled={!table.getCanPreviousPage()} + > + + + + + Page + + {`${ + table.getState().pagination.pageIndex + 1 + } of ${table.getPageCount()}`} + + + + + Go to page: + { + const page = e.target.value ? Number(e.target.value) - 1 : 0; + table.setPageIndex(page); + }} + /> + + + table.nextPage()} + disabled={!table.getCanNextPage()} + > + + + table.setPageIndex(table.getPageCount() - 1)} + disabled={!table.getCanNextPage()} + > + + + + + + + + {Object.keys(rowSelection).length > 0 && ( + + )} + + + + ); +} diff --git a/packages/web-extension/src/pages/index.html b/packages/web-extension/src/pages/index.html new file mode 100644 index 0000000000..bac6dabc6f --- /dev/null +++ b/packages/web-extension/src/pages/index.html @@ -0,0 +1,9 @@ + + +rrweb + + +
+ + + diff --git a/packages/web-extension/src/pages/index.tsx b/packages/web-extension/src/pages/index.tsx new file mode 100644 index 0000000000..0114523911 --- /dev/null +++ b/packages/web-extension/src/pages/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ChakraProvider } from '@chakra-ui/react'; +import * as ReactDOM from 'react-dom/client'; +import { createHashRouter, RouterProvider } from 'react-router-dom'; +import App from './App'; + +const rootElement = document.getElementById('root'); +const router = createHashRouter([ + { + path: '/*', + element: , + }, +]); + +rootElement && + ReactDOM.createRoot(rootElement).render( + + + + + , + ); diff --git a/packages/web-extension/src/popup/App.tsx b/packages/web-extension/src/popup/App.tsx new file mode 100644 index 0000000000..a6bed7b3f2 --- /dev/null +++ b/packages/web-extension/src/popup/App.tsx @@ -0,0 +1,249 @@ +import { useState, useEffect } from 'react'; +import { + Box, + Flex, + IconButton, + Link, + Spacer, + Stack, + Text, +} from '@chakra-ui/react'; +import { FiSettings, FiList, FiPause, FiPlay } from 'react-icons/fi'; +import Channel from '~/utils/channel'; +import { + LocalData, + LocalDataKey, + RecorderStatus, + ServiceName, + RecordStartedMessage, + RecordStoppedMessage, + Session, + EventName, +} from '~/types'; +import Browser from 'webextension-polyfill'; +import { CircleButton } from '~/components/CircleButton'; +import { Timer } from './Timer'; +import { pauseRecording, resumeRecording } from '~/utils/recording'; +import { saveSession } from '~/utils/storage'; +const RECORD_BUTTON_SIZE = 3; + +const channel = new Channel(); + +export function App() { + const [status, setStatus] = useState(RecorderStatus.IDLE); + const [errorMessage, setErrorMessage] = useState(''); + const [startTime, setStartTime] = useState(0); + const [newSession, setNewSession] = useState(null); + + useEffect(() => { + void Browser.storage.local.get(LocalDataKey.recorderStatus).then((data) => { + const localData = data as LocalData; + if (!localData || !localData[LocalDataKey.recorderStatus]) return; + const { status, startTimestamp, pausedTimestamp } = localData[ + LocalDataKey.recorderStatus + ]; + setStatus(status); + if (startTimestamp && pausedTimestamp) + setStartTime(Date.now() - pausedTimestamp + startTimestamp || 0); + else if (startTimestamp) setStartTime(startTimestamp); + }); + }, []); + + return ( + + + + RRWeb Recorder + + + + { + void Browser.tabs.create({ url: '/pages/index.html#/' }); + }} + size="xs" + icon={} + aria-label={'Session List'} + title="Session List" + > + { + void Browser.runtime.openOptionsPage(); + }} + size="xs" + icon={} + aria-label={'Settings button'} + title="Settings" + > + + + {status !== RecorderStatus.IDLE && startTime && ( + + )} + + {[RecorderStatus.IDLE, RecorderStatus.RECORDING].includes(status) && ( + { + if (status === RecorderStatus.RECORDING) { + // stop recording + setErrorMessage(''); + void channel.getCurrentTabId().then((tabId) => { + if (tabId === -1) return; + void channel + .requestToTab(tabId, ServiceName.StopRecord, {}) + .then(async (res: RecordStoppedMessage) => { + if (!res) return; + + setStatus(RecorderStatus.IDLE); + const status: LocalData[LocalDataKey.recorderStatus] = { + status: RecorderStatus.IDLE, + activeTabId: tabId, + }; + await Browser.storage.local.set({ + [LocalDataKey.recorderStatus]: status, + }); + if (res.session) { + setNewSession(res.session); + await saveSession(res.session, res.events).catch( + (e) => { + setErrorMessage((e as { message: string }).message); + }, + ); + channel.emit(EventName.SessionUpdated, {}); + } + }) + .catch((error: Error) => { + setErrorMessage(error.message); + }); + }); + } else { + // start recording + void channel.getCurrentTabId().then((tabId) => { + if (tabId === -1) return; + void channel + .requestToTab(tabId, ServiceName.StartRecord, {}) + .then(async (res: RecordStartedMessage | undefined) => { + if (res) { + setStatus(RecorderStatus.RECORDING); + setStartTime(res.startTimestamp); + const status: LocalData[LocalDataKey.recorderStatus] = { + status: RecorderStatus.RECORDING, + activeTabId: tabId, + startTimestamp: res.startTimestamp, + }; + await Browser.storage.local.set({ + [LocalDataKey.recorderStatus]: status, + }); + } + }) + .catch((error: Error) => { + setErrorMessage(error.message); + }); + }); + } + }} + > + + + )} + {status !== RecorderStatus.IDLE && ( + { + if (status === RecorderStatus.RECORDING) { + void pauseRecording(channel, RecorderStatus.PAUSED).then( + (result) => { + if (!result) return; + setStatus(result?.status.status); + }, + ); + } else { + void channel.getCurrentTabId().then((tabId) => { + if (tabId === -1) return; + resumeRecording(channel, tabId) + .then((statusData) => { + if (!statusData) return; + setStatus(statusData.status); + if (statusData.startTimestamp) + setStartTime(statusData.startTimestamp); + }) + .catch((error: Error) => { + setErrorMessage(error.message); + }); + }); + } + }} + > + + {[RecorderStatus.PAUSED, RecorderStatus.PausedSwitch].includes( + status, + ) && ( + + )} + {status === RecorderStatus.RECORDING && ( + + )} + + + )} + + {newSession && ( + + New Session: + + {newSession.name} + + + )} + {errorMessage !== '' && ( + + {errorMessage} +
+ Maybe refresh your current tab. +
+ )} +
+ ); +} diff --git a/packages/web-extension/src/popup/Timer.tsx b/packages/web-extension/src/popup/Timer.tsx new file mode 100644 index 0000000000..d8732e2e9b --- /dev/null +++ b/packages/web-extension/src/popup/Timer.tsx @@ -0,0 +1,25 @@ +import { useEffect, useState } from 'react'; +import { Stat, StatNumber } from '@chakra-ui/react'; +import { formatTime } from '~/utils'; + +export function Timer({ + startTime, + ticking, +}: { + startTime: number; + ticking: boolean; +}) { + const [time, setTime] = useState(Date.now() - startTime); + useEffect(() => { + if (!ticking) return; + const interval = setInterval(() => { + setTime(Date.now() - startTime); + }, 100); + return () => clearInterval(interval); + }, [startTime, ticking]); + return ( + + {formatTime(time)} + + ); +} diff --git a/packages/web-extension/src/popup/index.tsx b/packages/web-extension/src/popup/index.tsx new file mode 100644 index 0000000000..d93c01a2b0 --- /dev/null +++ b/packages/web-extension/src/popup/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { ChakraProvider } from '@chakra-ui/react'; +import * as ReactDOM from 'react-dom/client'; +import { App } from './App'; + +const rootElement = document.getElementById('root'); + +rootElement && + ReactDOM.createRoot(rootElement).render( + + + + + , + ); diff --git a/packages/web-extension/src/popup/popup.html b/packages/web-extension/src/popup/popup.html new file mode 100644 index 0000000000..18065c296c --- /dev/null +++ b/packages/web-extension/src/popup/popup.html @@ -0,0 +1,8 @@ + + + + +
+ + + diff --git a/packages/web-extension/src/types.ts b/packages/web-extension/src/types.ts new file mode 100644 index 0000000000..f0dae57e43 --- /dev/null +++ b/packages/web-extension/src/types.ts @@ -0,0 +1,86 @@ +import type { eventWithTime } from '@rrweb/types'; + +export enum SyncDataKey { + settings = 'settings', +} + +export type SyncData = { + [SyncDataKey.settings]: Settings; +}; + +export type Settings = { + // +}; + +export enum LocalDataKey { + recorderStatus = 'recorder_status', + bufferedEvents = 'buffered_events', +} + +export type LocalData = { + [LocalDataKey.recorderStatus]: { + status: RecorderStatus; + activeTabId: number; + startTimestamp?: number; + // the timestamp when the recording is paused + pausedTimestamp?: number; + }; + [LocalDataKey.bufferedEvents]: eventWithTime[]; +}; + +export enum RecorderStatus { + IDLE = 'IDLE', + RECORDING = 'RECORDING', + PAUSED = 'PAUSED', + // when user change the tab, the recorder will be paused during the tab change + PausedSwitch = 'PAUSED_SWITCH', +} + +export type Session = { + id: string; + name: string; + tags: string[]; + createTimestamp: number; + modifyTimestamp: number; + recorderVersion: string; +}; + +// all service names for channel +export enum ServiceName { + StartRecord = 'start-record', + StopRecord = 'stop-record', + PauseRecord = 'pause-record', + ResumeRecord = 'resume-record', +} + +// all event names for channel +export enum EventName { + SessionUpdated = 'session-updated', +} + +// all message names for postMessage API +export enum MessageName { + RecordScriptReady = 'rrweb-extension-record-script-ready', + StartRecord = 'rrweb-extension-start-record', + RecordStarted = 'rrweb-extension-record-started', + StopRecord = 'rrweb-extension-stop-record', + RecordStopped = 'rrweb-extension-record-stopped', + EmitEvent = 'rrweb-extension-emit-event', +} + +export type RecordStartedMessage = { + message: MessageName.RecordStarted; + startTimestamp: number; +}; + +export type RecordStoppedMessage = { + message: MessageName.RecordStopped; + events: eventWithTime[]; + endTimestamp: number; + session?: Session; +}; + +export type EmitEventMessage = { + message: MessageName.EmitEvent; + event: eventWithTime; +}; diff --git a/packages/web-extension/src/utils/channel.ts b/packages/web-extension/src/utils/channel.ts new file mode 100644 index 0000000000..a1c0ea8eb7 --- /dev/null +++ b/packages/web-extension/src/utils/channel.ts @@ -0,0 +1,180 @@ +import mitt from 'mitt'; +import Browser, { Runtime } from 'webextension-polyfill'; + +export type Message = EventType | ServiceType; +export type EventType = { + type: 'event'; + event: string; + detail: unknown; +}; +export type ServiceType = { + type: 'service'; + service: string; + params: unknown; +}; + +/** + * Channel for inter-context communication. + * + * A chrome extension typically contains 4 types of context: background, popup, options and content scripts. + * Communication between these contexts relies on + * chrome.runtime.sendMessage and chrome.tabs.sendMessage. + * + * This Class provides two communication model: + * * request/response + * * event trigger/listen + * based on chrome.runtime.sendMessage and chrome.tabs.sendMessage. + */ +class Channel { + private services = new Map< + string, + (params: unknown, sender: Runtime.MessageSender) => Promise + >(); + private emitter = mitt(); + constructor() { + /** + * Register massage listener. + */ + Browser.runtime.onMessage.addListener( + ((message: string, sender: Runtime.MessageSender) => { + const parsed = JSON.parse(message) as Message | null | undefined; + if (!parsed || !parsed.type) { + console.error(`Bad message: ${message}`); + return; + } + switch (parsed.type) { + case 'event': + this.emitter.emit(parsed.event, { detail: parsed.detail, sender }); + break; + case 'service': { + const server = this.services.get(parsed.service); + if (!server) break; + return server(parsed.params, sender); + } + default: + console.error( + `Unknown message type: ${(parsed as { type: string }).type}`, + ); + break; + } + return; + }).bind(this), + ); + } + + /** + * Provide a service. + * + * @param serviceName - the name of the service, acts like a URL + * @param serveFunction - a function to provide the service when a consumer request this service. + * @returns a function to remove the service + */ + public provide( + serviceName: string, + serveFunction: ( + params: unknown, + sender: Runtime.MessageSender, + ) => Promise, + ): () => void { + this.services.set(serviceName, serveFunction); + return () => { + this.services.delete(serviceName); + }; + } + + /** + * Send a request and get a response. + * + * @param service - service name to request + * @param params - request parameters + * @returns service data + */ + public request( + serviceName: string, + params: Record | unknown, + ) { + const message = JSON.stringify({ + type: 'service', + service: serviceName, + params, + }); + return Browser.runtime.sendMessage(message); + } + + /** + * Send a request to the specified tab and get a response. + * + * @param tabId - tab id + * @param service - service name to request + * @param params - request parameters + * @returns service data + */ + public requestToTab( + tabId: number, + serviceName: string, + params: Record | unknown, + ) { + if (!Browser.tabs || !Browser.tabs.sendMessage) + return Promise.reject('Can not send message to tabs in current context!'); + const message = JSON.stringify({ + type: 'service', + service: serviceName, + params, + }); + return Browser.tabs.sendMessage(tabId, message); + } + + /** + * Add an event handler. + * + * @param eventName - event name + * @param handler - event handler, accepts two arguments: + * detail: event detail + * source: source of the event, chrome.runtime.MessageSender object + * @returns a function to remove the handler + */ + public on(event: string, handler: (detail: unknown) => unknown) { + return this.emitter.on(event, handler); + } + + /** + * Emit an event. + * + * @param event - event name + * @param detail - event detail + */ + public emit(event: string, detail: unknown) { + const message = JSON.stringify({ type: 'event', event, detail }); + void Browser.runtime.sendMessage(message); + } + + /** + * Emit an event to specified tabs. + * + * @param tabIds - tab ids + * @param event - event name + * @param detail - event detail + */ + public emitToTabs(tabIds: number | number[], event: string, detail: unknown) { + if (!Browser.tabs || !Browser.tabs.sendMessage) + return Promise.reject('Can not send message to tabs in current context!'); + + // If tabIds is a number, wrap it up with an array. + if (typeof tabIds === 'number') { + tabIds = [tabIds]; + } + + const message = JSON.stringify({ type: 'event', event, detail }); + tabIds.forEach((tabId) => void Browser.tabs.sendMessage(tabId, message)); + } + + public async getCurrentTabId() { + const tabs = await Browser.tabs.query({ + active: true, + currentWindow: true, + }); + return tabs[0].id || -1; + } +} + +export default Channel; diff --git a/packages/web-extension/src/utils/index.ts b/packages/web-extension/src/utils/index.ts new file mode 100644 index 0000000000..1386e802fa --- /dev/null +++ b/packages/web-extension/src/utils/index.ts @@ -0,0 +1,44 @@ +export function isFirefox(): boolean { + return window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1; +} + +export function isInCrossOriginIFrame(): boolean { + if (window.parent !== window) { + try { + void window.parent.location.origin; + } catch (error) { + return true; + } + } + return false; +} + +const SECOND = 1000; +const MINUTE = 60 * SECOND; +const HOUR = 60 * MINUTE; + +export function formatTime(ms: number): string { + if (ms <= 0) { + return '00:00'; + } + const hour = Math.floor(ms / HOUR); + ms = ms % HOUR; + const minute = Math.floor(ms / MINUTE); + ms = ms % MINUTE; + const second = Math.floor(ms / SECOND); + if (hour) { + return `${padZero(hour)}:${padZero(minute)}:${padZero(second)}`; + } + return `${padZero(minute)}:${padZero(second)}`; +} + +function padZero(num: number, len = 2): string { + let str = String(num); + const threshold = Math.pow(10, len - 1); + if (num < threshold) { + while (String(threshold).length > str.length) { + str = `0${num}`; + } + } + return str; +} diff --git a/packages/web-extension/src/utils/recording.ts b/packages/web-extension/src/utils/recording.ts new file mode 100644 index 0000000000..69fc8362ea --- /dev/null +++ b/packages/web-extension/src/utils/recording.ts @@ -0,0 +1,90 @@ +import Browser from 'webextension-polyfill'; +import type { eventWithTime } from '@rrweb/types'; + +import { + LocalData, + LocalDataKey, + RecorderStatus, + RecordStartedMessage, + RecordStoppedMessage, + ServiceName, +} from '~/types'; +import type Channel from './channel'; +import { isFirefox } from '.'; + +/** + * Some commonly used functions for session recording. + */ + +// Pause recording. +export async function pauseRecording( + channel: Channel, + newStatus: RecorderStatus, + status?: LocalData[LocalDataKey.recorderStatus], +) { + if (!status) + status = (await Browser.storage.local.get(LocalDataKey.recorderStatus))[ + LocalDataKey.recorderStatus + ] as LocalData[LocalDataKey.recorderStatus]; + const { startTimestamp, activeTabId } = status; + const stopResponse = (await channel.requestToTab( + activeTabId, + ServiceName.PauseRecord, + {}, + )) as RecordStoppedMessage; + if (!stopResponse) return; + const statusData: LocalData[LocalDataKey.recorderStatus] = { + status: newStatus, + activeTabId, + startTimestamp, + pausedTimestamp: stopResponse.endTimestamp, + }; + await Browser.storage.local.set({ + [LocalDataKey.recorderStatus]: statusData, + }); + return { + status: statusData, + bufferedEvents: stopResponse.events, + }; +} + +// Resume recording after change to a new tab. +export async function resumeRecording( + channel: Channel, + newTabId: number, + status?: LocalData[LocalDataKey.recorderStatus], + bufferedEvents?: eventWithTime[], +) { + if (!status) + status = (await Browser.storage.local.get(LocalDataKey.recorderStatus))[ + LocalDataKey.recorderStatus + ] as LocalData[LocalDataKey.recorderStatus]; + if (!bufferedEvents) + bufferedEvents = ( + (await Browser.storage.local.get( + LocalDataKey.bufferedEvents, + )) as LocalData + )[LocalDataKey.bufferedEvents]; + const { startTimestamp, pausedTimestamp } = status; + // On Firefox, the new tab is not communicable immediately after it is created. + if (isFirefox()) await new Promise((r) => setTimeout(r, 50)); + const startResponse = (await channel.requestToTab( + newTabId, + ServiceName.ResumeRecord, + { events: bufferedEvents, pausedTimestamp }, + )) as RecordStartedMessage; + if (!startResponse) return; + const pausedTime = pausedTimestamp + ? startResponse.startTimestamp - pausedTimestamp + : 0; + const statusData: LocalData[LocalDataKey.recorderStatus] = { + status: RecorderStatus.RECORDING, + activeTabId: newTabId, + startTimestamp: + (startTimestamp || bufferedEvents[0].timestamp) + pausedTime, + }; + await Browser.storage.local.set({ + [LocalDataKey.recorderStatus]: statusData, + }); + return statusData; +} diff --git a/packages/web-extension/src/utils/storage.ts b/packages/web-extension/src/utils/storage.ts new file mode 100644 index 0000000000..9c52c38a05 --- /dev/null +++ b/packages/web-extension/src/utils/storage.ts @@ -0,0 +1,90 @@ +import { openDB } from 'idb'; +import { eventWithTime } from '@rrweb/types'; +import { Session } from '~/types'; + +/** + * Storage related functions with indexedDB. + */ + +const EventStoreName = 'events'; +type EventData = { + id: string; + events: eventWithTime[]; +}; + +export async function getEventStore() { + return openDB(EventStoreName, 1, { + upgrade(db) { + db.createObjectStore(EventStoreName, { + keyPath: 'id', + autoIncrement: false, + }); + }, + }); +} + +export async function getEvents(id: string) { + const db = await getEventStore(); + const data = (await db.get(EventStoreName, id)) as EventData; + return data.events; +} + +const SessionStoreName = 'sessions'; +export async function getSessionStore() { + return openDB(SessionStoreName, 1, { + upgrade(db) { + // Create a store of objects + db.createObjectStore(SessionStoreName, { + // The 'id' property of the object will be the key. + keyPath: 'id', + // If it isn't explicitly set, create a value by auto incrementing. + autoIncrement: false, + }); + }, + }); +} + +export async function saveSession(session: Session, events: eventWithTime[]) { + const eventStore = await getEventStore(); + await eventStore.put(EventStoreName, { id: session.id, events }); + const store = await getSessionStore(); + await store.add(SessionStoreName, session); +} + +export async function getSession(id: string) { + const store = await getSessionStore(); + return store.get(SessionStoreName, id) as Promise; +} + +export async function getAllSessions() { + const store = await getSessionStore(); + const sessions = (await store.getAll(SessionStoreName)) as Session[]; + return sessions.sort((a, b) => b.createTimestamp - a.createTimestamp); +} + +export async function deleteSession(id: string) { + const eventStore = await getEventStore(); + const sessionStore = await getSessionStore(); + await Promise.all([ + eventStore.delete(EventStoreName, id), + sessionStore.delete(SessionStoreName, id), + ]); +} + +export async function deleteSessions(ids: string[]) { + const eventStore = await getEventStore(); + const sessionStore = await getSessionStore(); + const eventTransition = eventStore.transaction(EventStoreName, 'readwrite'); + const sessionTransition = sessionStore.transaction( + SessionStoreName, + 'readwrite', + ); + const promises = []; + for (const id of ids) { + promises.push(eventTransition.store.delete(id)); + promises.push(sessionTransition.store.delete(id)); + } + await Promise.all(promises).then(() => { + return Promise.all([eventTransition.done, sessionTransition.done]); + }); +} diff --git a/packages/web-extension/tsconfig.json b/packages/web-extension/tsconfig.json new file mode 100644 index 0000000000..999bfaa228 --- /dev/null +++ b/packages/web-extension/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "module": "ESNext", + "target": "es2016", + "lib": [ + "DOM", + "ESNext" + ], + "strict": true, + "esModuleInterop": true, + "incremental": false, + "skipLibCheck": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "noUnusedLocals": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "~/*": [ + "src/*" + ] + }, + "jsx": "react-jsx" + }, + "exclude": [ + "dist", + "node_modules" + ], + "references": [ + { + "path": "../rrweb" + }, + { + "path": "../rrweb-player" + }, + { + "path": "../types" + } + ] +} diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts new file mode 100644 index 0000000000..d46a8a0d43 --- /dev/null +++ b/packages/web-extension/vite.config.ts @@ -0,0 +1,106 @@ +import { + defineConfig, + LibraryFormats, + LibraryOptions, + PluginOption, +} from 'vite'; +import webExtension, { readJsonFile } from 'vite-plugin-web-extension'; +import zip from 'vite-plugin-zip'; +import * as path from 'path'; +import type { PackageJson } from 'type-fest'; +import react from '@vitejs/plugin-react'; + +function useSpecialFormat( + entriesToUse: string[], + format: LibraryFormats, +): PluginOption { + return { + name: 'use-special-format', + config(config) { + const shouldUse = entriesToUse.includes( + (config.build?.lib as LibraryOptions)?.entry, + ); + if (shouldUse) { + config.build ??= {}; + // @ts-expect-error: lib needs to be an object, forcing it. + config.build.lib ||= {}; + // @ts-expect-error: lib is an object + config.build.lib.formats = [format]; + } + }, + }; +} + +export default defineConfig({ + root: 'src', + // Configure our outputs - nothing special, this is normal vite config + build: { + outDir: path.resolve( + __dirname, + 'dist', + process.env.TARGET_BROWSER as string, + ), + emptyOutDir: true, + }, + // Add the webExtension plugin + plugins: [ + react(), + webExtension({ + // A function to generate manifest file dynamically. + manifest: () => { + const packageJson = readJsonFile('package.json') as PackageJson; + const isProduction = process.env.NODE_ENV === 'production'; + type ManifestBase = { + common: Record; + chrome: Record; + firefox: Record; + }; + const originalManifest = readJsonFile('./src/manifest.json') as { + common: Record; + v2: ManifestBase; + v3: ManifestBase; + }; + const ManifestVersion = + process.env.TARGET_BROWSER === 'chrome' && isProduction ? 'v3' : 'v2'; + const BrowserName = + process.env.TARGET_BROWSER === 'chrome' ? 'chrome' : 'firefox'; + const commonManifest = originalManifest.common; + const manifest = { + version: packageJson.version, + author: packageJson.author, + version_name: packageJson.dependencies?.rrweb?.replace('^', ''), + ...commonManifest, + }; + Object.assign( + manifest, + originalManifest[ManifestVersion].common, + originalManifest[ManifestVersion][BrowserName], + ); + return manifest; + }, + assets: 'assets', + browser: process.env.TARGET_BROWSER, + webExtConfig: { + startUrl: ['github.com/rrweb-io/rrweb'], + watchIgnored: ['*.md', '*.log'], + }, + additionalInputs: ['pages/index.html', 'content/inject.ts'], + }), + // https://github.com/aklinker1/vite-plugin-web-extension/issues/50#issuecomment-1317922947 + // transfer inject.ts to iife format to avoid error + useSpecialFormat( + [path.resolve(__dirname, 'src/content/inject.ts')], + 'iife', + ), + process.env.ZIP === 'true' && + zip({ + dir: 'dist', + outputName: process.env.TARGET_BROWSER, + }), + ], + resolve: { + alias: { + '~': path.resolve(__dirname, './src'), + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 44271ae283..0cf635e32d 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.19.3": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.4.tgz#95c86de137bf0317f3a570e1b6e996b427299747" + integrity sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw== + "@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" @@ -68,6 +88,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.18.13": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c" + integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.3" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-module-transforms" "^7.19.0" + "@babel/helpers" "^7.19.0" + "@babel/parser" "^7.19.3" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.3" + "@babel/types" "^7.19.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/generator@^7.15.4", "@babel/generator@^7.7.2": version "7.15.4" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz" @@ -86,6 +127,22 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.19.3", "@babel/generator@^7.19.4": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.5.tgz#da3f4b301c8086717eee9cab14da91b1fa5dcca7" + integrity sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg== + dependencies: + "@babel/types" "^7.19.4" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + "@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 +163,16 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.19.3": + version "7.19.3" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca" + integrity sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg== + dependencies: + "@babel/compat-data" "^7.19.3" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + 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 +180,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 +203,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 +239,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 +267,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 +302,20 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-module-transforms@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" + integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + "@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 +328,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", "@babel/helper-plugin-utils@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" + integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== + "@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 +357,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-simple-access@^7.18.6": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz#be553f4951ac6352df2567f7daa19a0ee15668e7" + integrity sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg== + dependencies: + "@babel/types" "^7.19.4" + "@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 +378,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 +405,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 +420,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 +443,15 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helpers@^7.19.0": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.4.tgz#42154945f87b8148df7203a25c31ba9a73be46c5" + integrity sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.4" + "@babel/types" "^7.19.4" + "@babel/highlight@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz" @@ -319,6 +470,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 +489,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz" integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA== +"@babel/parser@^7.18.10", "@babel/parser@^7.19.3", "@babel/parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.4.tgz#03c4339d2b8971eb3beca5252bafd9b9f79db3dc" + integrity sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA== + "@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 +529,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.17.12", "@babel/plugin-syntax-jsx@^7.18.6": + 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" @@ -420,6 +592,52 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx-self@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7" + integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-source@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc" + integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.18.10", "@babel/plugin-transform-react-jsx@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" + integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.19.0" + +"@babel/runtime@7.13.9": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee" + integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" + integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.20.1", "@babel/runtime@^7.5.5": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" @@ -445,6 +663,15 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + "@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 +703,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.3", "@babel/traverse@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.4.tgz#f117820e18b1e59448a6c1fa9d0ff08f7ac459a8" + integrity sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.4" + "@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.19.4" + "@babel/types" "^7.19.4" + 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,11 +735,748 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7" + integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw== + 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" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@chakra-ui/accordion@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-2.1.1.tgz#34a8ffe7eb5bd30f7e10db4ed61f80c53d8bfa18" + integrity sha512-5f4QBl/0EgU/9EVvzlj8ZU7SWwG6nUHCE9moGBCbgiIOVBEySxZ5Robsk6+T7sXmzQ41db04GcUE9NRKdalgIA== + dependencies: + "@chakra-ui/descendant" "3.0.10" + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/transition" "2.0.10" + +"@chakra-ui/alert@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/alert/-/alert-2.0.11.tgz#d792b0684ae7810befa3874af5bdd4aa115513a2" + integrity sha512-n40KHU3j1H6EbIdgptjEad92V7Fpv7YD++ZBjy2g1h4w9ay9nw4kGHib3gaIkBupLf52CfLqySEc8w0taoIlXQ== + dependencies: + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/spinner" "2.0.10" + +"@chakra-ui/anatomy@2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@chakra-ui/anatomy/-/anatomy-2.0.7.tgz#33e60c7c4d6e5f949f6f8308249dc571f84ead1e" + integrity sha512-vzcB2gcsGCxhrKbldQQV6LnBPys4eSSsH2UA2mLsT+J3WlXw0aodZw0eE/nH7yLxe4zaQ4Gnc0KjkFW4EWNKSg== + +"@chakra-ui/avatar@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/avatar/-/avatar-2.1.1.tgz#7482d7cda4736dfa9e4b4eefd5cbb8c5b1486c64" + integrity sha512-lTZPUq4Pefxgv3ndyJMxIHgFrXwdz2VZFCLF/aKcuGaUlB7TBYaCurQ7TNbME8j8VkJWNX+vKiVHPYvxsrITwQ== + dependencies: + "@chakra-ui/image" "2.0.11" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/breadcrumb@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/breadcrumb/-/breadcrumb-2.0.10.tgz#2b01e7422e171d1de1a8ca8c8e2a774c8449701b" + integrity sha512-roKFA7nheq18eWNAdrHV6w8A9vZMSQTEEsbL6eU0lhUkolW9RlDjBl1bZvE7icFkNFXlJ33n8+0QAezLI+mMrQ== + dependencies: + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/breakpoint-utils@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.4.tgz#6231eff8b20f4e3cbb4eb7c86d05c927679d905b" + integrity sha512-SUUEYnA/FCIKYDHMuEXcnBMwet+6RAAjQ+CqGD1hlwKPTfh7EK9fS8FoVAJa9KpRKAc/AawzPkgwvorzPj8NSg== + +"@chakra-ui/button@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/button/-/button-2.0.11.tgz#98e0aa1e35ea7e193bb50f9a4b5d0ea23202ace8" + integrity sha512-J6iMRITqxTxa0JexHUY9c7BXUrTZtSkl3jZ2hxiFybB4MQL8J2wZ24O846B6M+WTYqy7XVuHRuVURnH4czWesw== + dependencies: + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/spinner" "2.0.10" + +"@chakra-ui/checkbox@2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/checkbox/-/checkbox-2.2.1.tgz#3e88337f4588bb8bbc37da0102653b35991d736d" + integrity sha512-soTeXEI+4UZSA4B4rRLpdh3cIW/gdhY6k0eXF4ZWExPb+dJ5Giv497S96vS4IGE7SJ7Ugw9kaWS+do2lSiPJew== + dependencies: + "@chakra-ui/form-control" "2.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-callback-ref" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + "@chakra-ui/react-use-update-effect" "2.0.4" + "@chakra-ui/visually-hidden" "2.0.11" + "@zag-js/focus-visible" "0.1.0" + +"@chakra-ui/clickable@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/clickable/-/clickable-2.0.10.tgz#e89b7b3eaf9364753f6205e36fd5128b26a617d8" + integrity sha512-G6JdR6yAMlXpfjOJ70W2FL7aUwNuomiMFtkneeTpk7Q42bJ5iGHfYlbZEx5nJd8iB+UluXVM4xlhMv2MyytjGw== + dependencies: + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/close-button@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/close-button/-/close-button-2.0.11.tgz#8b0679da42738229014d3807885d05fac0fdf448" + integrity sha512-9WF/nwwK9BldS89WQ5PtXK2nFS4r8QOgKls2BOwXfE+rGmOUZtOsu8ne/drXRjgkiBRETR6CxdyUjm7EPzXllw== + dependencies: + "@chakra-ui/icon" "3.0.11" + +"@chakra-ui/color-mode@2.1.9": + version "2.1.9" + resolved "https://registry.yarnpkg.com/@chakra-ui/color-mode/-/color-mode-2.1.9.tgz#d3a6f9ba9eee15d9e14cc96484e25d44cef1dbc1" + integrity sha512-0kx0I+AQon8oS23/X+qMtnhsv/1BUulyJvU56p3Uh8CRaBfgJ7Ly9CerShoUL+5kadu6hN1M9oty4cugaCwv2w== + dependencies: + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + +"@chakra-ui/control-box@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/control-box/-/control-box-2.0.10.tgz#e8a849c9f0fa085da78ee15dda7e13e1734b983d" + integrity sha512-sHmZanFLEv4IDATl19ZTxq8Bi8PtjfvnsN6xF4k7JGSYUnk1YXUf1coyW7WKdcsczOASrMikfsLc3iEVAzx4Ng== + +"@chakra-ui/counter@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/counter/-/counter-2.0.10.tgz#861f00db021235892dfe0407e739a259f1c233b2" + integrity sha512-MZK8UKUZp4nFMd+GlV/cq0NIARS7UdlubTuCx+wockw9j2JI5OHzsyK0XiWuJiq5psegSTzpbtT99QfAUm3Yiw== + dependencies: + "@chakra-ui/number-utils" "2.0.4" + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/css-reset@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@chakra-ui/css-reset/-/css-reset-2.0.8.tgz#093ce6b166b37f2dd14e63f246635c463a59c106" + integrity sha512-VuDD1rk1pFc+dItk4yUcstyoC9D2B35hatHDBtlPMqTczFAzpbgVJJYgEHANatXGfulM5SdckmYEIJ3Tac1Rtg== + +"@chakra-ui/descendant@3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/descendant/-/descendant-3.0.10.tgz#e54c95270896c451f61b57d31719ee042f4e1827" + integrity sha512-MHH0Qdm0fGllGP2xgx4WOycmrpctyyEdGw6zxcfs2VqZNlrwmjG3Yb9eVY+Q7UmEv5rwAq6qRn7BhQxgSPn3Cg== + dependencies: + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/dom-utils@2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@chakra-ui/dom-utils/-/dom-utils-2.0.3.tgz#8a5498b107d3a42662f3502f7b8965cb73bf6a33" + integrity sha512-aeGlRmTxcv0cvW44DyeZHru1i68ZDQsXpfX2dnG1I1yBlT6GlVx1xYjCULis9mjhgvd2O3NfcYPRTkjNWTDUbA== + +"@chakra-ui/editable@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/editable/-/editable-2.0.12.tgz#935d100cdce18268c9bf716ec9192b4b46253ffb" + integrity sha512-37bDqm+j2JTN2XR443KRK9MmHHIQuS6fN+2TRuFgjfG8TomxxCJnhJ3GIfQSKh5Yjtnt4sXDmL4L0tyDpNrrrw== + dependencies: + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-callback-ref" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-focus-on-pointer-down" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + "@chakra-ui/react-use-update-effect" "2.0.4" + "@chakra-ui/shared-utils" "2.0.2" + +"@chakra-ui/event-utils@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/event-utils/-/event-utils-2.0.5.tgz#23de21e319d1a70863953402d64cb4b0e6ce322f" + integrity sha512-VXoOAIsM0PFKDlhm+EZxkWlUXd5UFTb/LTux3y3A+S9G5fDxLRvpiLWByPUgTFTCDFcgTCF+YnQtdWJB4DLyxg== + +"@chakra-ui/focus-lock@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/focus-lock/-/focus-lock-2.0.12.tgz#11c0301a326249efe269c2dd0f54b11a67a04321" + integrity sha512-NvIP59A11ZNbxXZ3qwxSiQ5npjABkpSbTIjK0uZ9bZm5LMfepRnuuA19VsVlq31/BYV9nHFAy6xzIuG+Qf9xMA== + dependencies: + "@chakra-ui/dom-utils" "2.0.3" + react-focus-lock "^2.9.1" + +"@chakra-ui/form-control@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/form-control/-/form-control-2.0.11.tgz#fbfdddb02d1b5d2c67ffdc721c434ff16693e4bd" + integrity sha512-MVhIe0xY4Zn06IXRXFmS9tCa93snppK1SdUQb1P99Ipo424RrL5ykzLnJ8CAkQrhoVP3sxF7z3eOSzk8/iRfow== + dependencies: + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/hooks@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/hooks/-/hooks-2.0.11.tgz#465ee4720c0e76900a4a4480c347d6c91bb4848e" + integrity sha512-mYN4u9lbjDjEr/VucrVcLGg/sIO6gA9ZprcT3n9CBGSWt3xih7fCOJmE+yRcCNbL7335AMrv7a/M5Q30aRArcA== + dependencies: + "@chakra-ui/react-utils" "2.0.8" + "@chakra-ui/utils" "2.0.11" + compute-scroll-into-view "1.0.14" + copy-to-clipboard "3.3.1" + +"@chakra-ui/icon@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/icon/-/icon-3.0.11.tgz#a51dda24bed2f2ed77b4136ada8f22d3249c9870" + integrity sha512-RG4jf/XmBdaxOYI5J5QstEtTCPoVlmrQ/XiWhvN0LTgAnmZIqVwFl3Uw+satArdStHAs0GmJZg/E/soFTWuFmw== + dependencies: + "@chakra-ui/shared-utils" "2.0.2" + +"@chakra-ui/image@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/image/-/image-2.0.11.tgz#eb880ecd2fce47f22ef50bbbba66cbb027c0304c" + integrity sha512-S6NqAprPcbHnck/J+2wg06r9SSol62v5A01O8Kke2PnAyjalMcS+6P59lDRO7wvPqsdxq4PPbSTZP6Dww2CvcA== + dependencies: + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + +"@chakra-ui/input@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/input/-/input-2.0.11.tgz#1d37093f2128f3148551b22c43f623256c856940" + integrity sha512-kaV0VCz6/yzoCKQnh/tMUVgh+Rp6EnM+WzJ37SVX1gDvErON2bmmVLU45BiRoWUcd50wOhDlpsNVUWP0sLlCDA== + dependencies: + "@chakra-ui/form-control" "2.0.11" + "@chakra-ui/object-utils" "2.0.4" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/shared-utils" "2.0.2" + +"@chakra-ui/layout@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@chakra-ui/layout/-/layout-2.1.8.tgz#5bab204e33079527e0756e2f3616718888b5f14d" + integrity sha512-pcNUNgMh+e4wepNOlCg5iDrxGg4VFBpqZPmSHoP4TyPN2ddEnDRLoMLaREMoX7gEVyTsqEFOFg+wa3JZK32H4A== + dependencies: + "@chakra-ui/breakpoint-utils" "2.0.4" + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/object-utils" "2.0.4" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/shared-utils" "2.0.2" + +"@chakra-ui/lazy-utils@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/lazy-utils/-/lazy-utils-2.0.2.tgz#d85f9afc60c2434ba76376fd4b23a7a0a1341e14" + integrity sha512-MTxutBJZvqNNqrrS0722cI7qrnGu0yUQpIebmTxYwI+F3cOnPEKf5Ni+hrA8hKcw4XJhSY4npAPPYu1zJbOV4w== + +"@chakra-ui/live-region@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/live-region/-/live-region-2.0.10.tgz#d33a784c85feed7ba96e2579553ca1d20c965171" + integrity sha512-eQ2ZIreR/plzi/KGszDYTi1TvIyGEBcPiWP52BQOS7xwpzb1vsoR1FgFAIELxAGJvKnMUs+9qVogfyRBX8PdOg== + +"@chakra-ui/media-query@3.2.7": + version "3.2.7" + resolved "https://registry.yarnpkg.com/@chakra-ui/media-query/-/media-query-3.2.7.tgz#ece5b2181136145305bf5e6ec82c696ef1d59a77" + integrity sha512-hbgm6JCe0kYU3PAhxASYYDopFQI26cW9kZnbp+5tRL1fykkVWNMPwoGC8FEZPur9JjXp7aoL6H4Jk7nrxY/XWw== + dependencies: + "@chakra-ui/breakpoint-utils" "2.0.4" + "@chakra-ui/react-env" "2.0.10" + +"@chakra-ui/menu@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/menu/-/menu-2.1.1.tgz#de2b6da5c1267513a7c4b2ae73eff6ea4848a326" + integrity sha512-9fpCyV3vufLV5Rvv/oYC3LyCIkNqh0bEdYFVOLiqCZ6mt6NLFxL2jgE25nROYfDXQuBkY0qPC9IopYU198G4nw== + dependencies: + "@chakra-ui/clickable" "2.0.10" + "@chakra-ui/descendant" "3.0.10" + "@chakra-ui/lazy-utils" "2.0.2" + "@chakra-ui/popper" "3.0.8" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-animation-state" "2.0.5" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-disclosure" "2.0.5" + "@chakra-ui/react-use-focus-effect" "2.0.5" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-outside-click" "2.0.4" + "@chakra-ui/react-use-update-effect" "2.0.4" + "@chakra-ui/transition" "2.0.10" + +"@chakra-ui/modal@2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/modal/-/modal-2.2.1.tgz#2903d1e0a0a5ab308db2ce5e3f6382eed20e702a" + integrity sha512-+zfiUG/yZqUQ0wY7syoZg01cpBf54lbKUe7+ANEx558UQGbsI4bbcHSkY9l5lsprQ8teInvhjb6BekeCA0e7TA== + dependencies: + "@chakra-ui/close-button" "2.0.11" + "@chakra-ui/focus-lock" "2.0.12" + "@chakra-ui/portal" "2.0.10" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/transition" "2.0.10" + aria-hidden "^1.1.1" + react-remove-scroll "^2.5.4" + +"@chakra-ui/number-input@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/number-input/-/number-input-2.0.12.tgz#90a8408e6abb2d021793888ef2119d01761d7614" + integrity sha512-3owLjl01sCYpTd3xbq//fJo9QJ0Q3PVYSx9JeOzlXnnTW8ws+yHPrqQzPe7G+tO4yOYynWuUT+NJ9oyCeAJIxA== + dependencies: + "@chakra-ui/counter" "2.0.10" + "@chakra-ui/form-control" "2.0.11" + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-callback-ref" "2.0.4" + "@chakra-ui/react-use-event-listener" "2.0.4" + "@chakra-ui/react-use-interval" "2.0.2" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + "@chakra-ui/react-use-update-effect" "2.0.4" + +"@chakra-ui/number-utils@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/number-utils/-/number-utils-2.0.4.tgz#0331be05956f2c03125c073d35655e261e267cd4" + integrity sha512-MdYd29GboBoKaXY9jhbY0Wl+0NxG1t/fa32ZSIbU6VrfMsZuAMl4NEJsz7Xvhy50fummLdKn5J6HFS7o5iyIgw== + +"@chakra-ui/object-utils@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/object-utils/-/object-utils-2.0.4.tgz#d890ce285103a5e9b993f016a4fb38307aa55ac0" + integrity sha512-sY98L4v2wcjpwRX8GCXqT+WzpL0i5FHVxT1Okxw0360T2tGnZt7toAwpMfIOR3dzkemP9LfXMCyBmWR5Hi2zpQ== + +"@chakra-ui/pin-input@2.0.14": + version "2.0.14" + resolved "https://registry.yarnpkg.com/@chakra-ui/pin-input/-/pin-input-2.0.14.tgz#8b5a1817c508bf0c986c6855245b0dc516a14df8" + integrity sha512-gFNlTUjU1xIuOErR/d/HrNNh1mS0erjNJSt5C6RU/My4lShzgCczmwnil7TuEx3k7lPqHKLEf/CGeCxBSUjaGA== + dependencies: + "@chakra-ui/descendant" "3.0.10" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/popover@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@chakra-ui/popover/-/popover-2.1.1.tgz#1b5e05e334ba5f9bce4bc5bcabfb92563393fc84" + integrity sha512-j09NsesfT+eaYITkITYJXDlRcPoOeQUM80neJZKOBgul2iHkVsEoii8dwS5Ip5ONeu4ane1b6zEOlYvYj2SrkA== + dependencies: + "@chakra-ui/close-button" "2.0.11" + "@chakra-ui/lazy-utils" "2.0.2" + "@chakra-ui/popper" "3.0.8" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-animation-state" "2.0.5" + "@chakra-ui/react-use-disclosure" "2.0.5" + "@chakra-ui/react-use-focus-effect" "2.0.5" + "@chakra-ui/react-use-focus-on-pointer-down" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/popper@3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@chakra-ui/popper/-/popper-3.0.8.tgz#89b6984aee405316974dbb70ba451f85832bf44e" + integrity sha512-246eUwuCRsLpTPxn5T8D8T9/6ODqmmz6pRRJAjGnLlUB0gNHgjisBn0UDBic5Gbxcg0sqKvxOMY3uurbW5lXTA== + dependencies: + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@popperjs/core" "^2.9.3" + +"@chakra-ui/portal@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/portal/-/portal-2.0.10.tgz#8ac21131cb0666a0bf6565468b3f7e799ef3bc8d" + integrity sha512-VRYvVAggIuqIZ3IQ6XZ1b5ujjjOUgPk9PPdc9jssUngZa7RG+5NXNhgoM8a5TsXv6aPEolBOlDNWuxzRQ4RSSg== + dependencies: + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + +"@chakra-ui/progress@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/progress/-/progress-2.0.11.tgz#f17a08f6d5dd59334ad705501debcbc031e0843d" + integrity sha512-2OwxGxI6W757QpDB6b++B4b2+t0oBgaQdHnd4/y35n3+mOFj++Wg7XpW4/iDHn+x3LkM+X3NIgdBWQFlmGx+6w== + dependencies: + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/provider@2.0.19": + version "2.0.19" + resolved "https://registry.yarnpkg.com/@chakra-ui/provider/-/provider-2.0.19.tgz#6364df6b1f5058f77c270030b122f6614cdb69c4" + integrity sha512-V+p0OePre0OgYmNxLbfiPWWbzaJ/EM2sfaRtD7E6ZA4TjUl2m4pWdC6OPMOiklu7EALfSgVk9X6Jh5pc+moH1g== + dependencies: + "@chakra-ui/css-reset" "2.0.8" + "@chakra-ui/portal" "2.0.10" + "@chakra-ui/react-env" "2.0.10" + "@chakra-ui/system" "2.2.12" + "@chakra-ui/utils" "2.0.11" + +"@chakra-ui/radio@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/radio/-/radio-2.0.12.tgz#d89eb463df0247a0e634cff1fb9ca755bcbab825" + integrity sha512-871hqAGQaufxyUzPP3aautPBIRZQmpi3fw5XPZ6SbY62dV61M4sjcttd46HfCf5SrAonoOADFQLMGQafznjhaA== + dependencies: + "@chakra-ui/form-control" "2.0.11" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@zag-js/focus-visible" "0.1.0" + +"@chakra-ui/react-children-utils@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-children-utils/-/react-children-utils-2.0.2.tgz#8676ce88573ad1824989399db8a1ec7e51afc0d0" + integrity sha512-mRTGAZ3DBXB3hgVwS2DVJe3Nlc0qGvMN0PAo4tD/3fj2op2IwspLcYPAWC5D/rI2xj2JlwE6szAtbvxdAfLCNw== + +"@chakra-ui/react-context@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-context/-/react-context-2.0.4.tgz#1b6ab260d44d9073c95b975b7d1643f011e65e02" + integrity sha512-eBITFkf7fLSiMZrSdhweK4fYr41WUNMEeIEOP2dCWolE7WgKxNYaYleC+iRGY0GeXkFM2KYywUtixjJe29NuVA== + +"@chakra-ui/react-env@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-env/-/react-env-2.0.10.tgz#2eaa4ba64a14ecd2d279c32d5edfef7a6b5de3e8" + integrity sha512-3Yab5EbFcCGYzEsoijy4eA3354Z/JoXyk9chYIuW7Uwd+K6g/R8C0mUSAHeTmfp6Fix9kzDgerO5MWNM87b8cA== + +"@chakra-ui/react-types@2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-types/-/react-types-2.0.3.tgz#dc454c4703b4de585e6461fd607304ede06fe595" + integrity sha512-1mJYOQldFTALE0Wr3j6tk/MYvgQIp6CKkJulNzZrI8QN+ox/bJOh8OVP4vhwqvfigdLTui0g0k8M9h+j2ub/Mw== + +"@chakra-ui/react-use-animation-state@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.0.5.tgz#f022baf0103c35aa494227b041422e7d2401b0d4" + integrity sha512-8gZIqZpMS5yTGlC+IqYoSrV13joiAYoeI0YR2t68WuDagcZ459OrjE57+gF04NLxfdV7eUgwqnpuv7IOLbJX/A== + dependencies: + "@chakra-ui/dom-utils" "2.0.3" + "@chakra-ui/react-use-event-listener" "2.0.4" + +"@chakra-ui/react-use-callback-ref@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.0.4.tgz#5099ef1df4413af42e434945f541de99394ec96f" + integrity sha512-he7EQfwMA4mwiDDKvX7cHIJaboCqf7UD3KYHGUcIjsF4dSc2Y8X5Ze4w+hmVZoJWIe4DWUzb3ili2SUm8eTgPg== + +"@chakra-ui/react-use-controllable-state@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.0.5.tgz#5ef9f600ae134a2a37fe080fd6231bbed83544bb" + integrity sha512-JrZZpMX24CUyfDuyqDczw9Z9IMvjH8ujETHK0Zu4M0SIsX/q4EqOwwngUFL03I2gx/O38HfSdeX8hMu4zbTAGA== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-disclosure@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.0.5.tgz#bb52340f0e7d614cc95819bd21cffd050783f96c" + integrity sha512-kPLB9oxImASRhAbKfvfc03/lbAJbsXndEVRzd+nvvL+QZm2RRfnel3k6OIkWvGFOXXYOPE2+slLe8ZPwbTGg9g== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-event-listener@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.0.4.tgz#3f893def57a7b10db6c355740dd1e82cd3216259" + integrity sha512-VqmalfKWMO8D21XuZO19WUtcP5xhbHXKzkggApTChZUN02UC5TC4pe0pYbDygoeUuNBhY+9lJKHeS08vYsljRg== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-focus-effect@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.0.5.tgz#b554277c38e84468b019e08a73579e9700e1003a" + integrity sha512-sbe1QnsXXfjukM+laxbKnT0UnMpHe/7kTzEPG/BYM6/ZDUUmrC1Nz+8l+3H/52iWIaruikDBdif/Xd37Yvu3Kg== + dependencies: + "@chakra-ui/dom-utils" "2.0.3" + "@chakra-ui/react-use-event-listener" "2.0.4" + "@chakra-ui/react-use-update-effect" "2.0.4" + +"@chakra-ui/react-use-focus-on-pointer-down@2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.0.3.tgz#8b605063c9e707a18b021fbcaed8919c8660d1ed" + integrity sha512-8cKmpv26JnblexNaekWxEDI7M+MZnJcp1PJUz6lByjfQ1m4YjFr1cdbdhG4moaqzzYs7vTmO/qL8KVq8ZLUwyQ== + dependencies: + "@chakra-ui/react-use-event-listener" "2.0.4" + +"@chakra-ui/react-use-interval@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-interval/-/react-use-interval-2.0.2.tgz#6d1d5d5b5c5604ee2ea47f1e140e6eaf6e885df5" + integrity sha512-5U1c0pEB5n0Yri0E4RdFXWx2RVBZBBhD8Uu49dM33jkIguCbIPmZ+YgVry5DDzCHyz4RgDg4yZKOPK0PI8lEUg== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-latest-ref@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.0.2.tgz#4895d3ae2dc93a660ed86aaec7021b729830d3d2" + integrity sha512-Ra/NMV+DSQ3n0AdKsyIqdgnFzls5UntabtIRfDXLrqmJ4tI0a1tDdop2qop0Ue87AcqD9P1KtQue4KPx7wCElw== + +"@chakra-ui/react-use-merge-refs@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.0.4.tgz#c23f10fda1d3a6327a48708a8a7ad4b62ba918d3" + integrity sha512-aoWvtE5tDQNaLCiNUI6WV+MA2zVcCLR5mHSCISmowlTXyXOqOU5Fo9ZoUftzrmgCJpDu5x1jfUOivxuHUueb0g== + +"@chakra-ui/react-use-outside-click@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.0.4.tgz#977d873cfedec615c8e3acd48fca7b094b464b6e" + integrity sha512-uerJKS8dqg2kHs1xozA5vcCqW0UInuwrfCPb+rDWBTpu7aEqxABMw9W3e4gfOABrAjhKz2I0a/bu2i8zbVwdLw== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-pan-event@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.0.5.tgz#9269d4b798d1447e18b00ee0b28fa52c5c8efb26" + integrity sha512-nhE3b85++EEmBD2v6m46TLoA4LehSCZ349P8kvEjw/RC0K6XDOZndaBucIeAlnpEENSSUpczFfMSOLxSHdu0oA== + dependencies: + "@chakra-ui/event-utils" "2.0.5" + "@chakra-ui/react-use-latest-ref" "2.0.2" + framesync "5.3.0" + +"@chakra-ui/react-use-previous@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-previous/-/react-use-previous-2.0.2.tgz#1091ae8abc2082ab504e3742f8b1d75409ae7b27" + integrity sha512-ap/teLRPKopaHYD80fnf0TR/NpTWHJO5VdKg6sPyF1y5ediYLAzPT1G2OqMCj4QfJsYDctioT142URDYe0Nn7w== + +"@chakra-ui/react-use-safe-layout-effect@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.0.2.tgz#31088eeb4b2a6910251683ddb15fb855d6127adf" + integrity sha512-gl5HDq9RVeDJiT8udtpx12KRV8JPLJHDIUX8f/yZcKpXow0C7FFGg5Yy5I9397NQog5ZjKMuOg+AUq9TLJxsyQ== + +"@chakra-ui/react-use-size@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-size/-/react-use-size-2.0.4.tgz#3634782f8dab6aa2a37699188afa89251cbae8f3" + integrity sha512-W6rgTLuoSC4ovZtqYco8cG+yBadH3bhlg92T5lgpKDakSDr0mXcZdbGx6g0AOkgxXm0V1jWNGO1743wudtF7ew== + dependencies: + "@zag-js/element-size" "0.1.0" + +"@chakra-ui/react-use-timeout@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-timeout/-/react-use-timeout-2.0.2.tgz#f1378de0d5e01f7aee60d5b9ec3205e1fc7d2fc4" + integrity sha512-n6zb3OmxtDmRMxYkDgILqKh15aDOa8jNLHBlqHzmlL6mEGNKmMFPW9j/KvpAqSgKjUTDRnnXcpneprTMKy/yrw== + dependencies: + "@chakra-ui/react-use-callback-ref" "2.0.4" + +"@chakra-ui/react-use-update-effect@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.0.4.tgz#522bc58b943fffe540a91f7a096d42e4a91b9748" + integrity sha512-F/I9LVnGAQyvww+x7tQb47wCwjhMYjpxtM1dTg1U3oCEXY0yF1Ts3NJLUAlsr3nAW6epJIwWx61niC7KWpam1w== + +"@chakra-ui/react-utils@2.0.8": + version "2.0.8" + resolved "https://registry.yarnpkg.com/@chakra-ui/react-utils/-/react-utils-2.0.8.tgz#1db4e920386f4afbf44fe9dd8aaaf6f22eefb371" + integrity sha512-OSHHBKZlJWTi2NZcPnBx1PyZvLQY+n5RPBtcri7/89EDdAwz2NdEhp2Dz1yQRctOSCF1kB/rnCYDP1U0oRk9RQ== + dependencies: + "@chakra-ui/utils" "2.0.11" + +"@chakra-ui/react@^2.3.4": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-2.3.5.tgz#0f11a6ede3e90e334bab604e7a2c56e0d0fa930e" + integrity sha512-bQDRV23M3IvF0+AorTvqJmG/4T6KKQIb+1XGA2RyxonoSHVt89HbN3qnygHJw06Sdgpclxdbr/1qZ4o8+SMbpA== + dependencies: + "@chakra-ui/accordion" "2.1.1" + "@chakra-ui/alert" "2.0.11" + "@chakra-ui/avatar" "2.1.1" + "@chakra-ui/breadcrumb" "2.0.10" + "@chakra-ui/button" "2.0.11" + "@chakra-ui/checkbox" "2.2.1" + "@chakra-ui/close-button" "2.0.11" + "@chakra-ui/control-box" "2.0.10" + "@chakra-ui/counter" "2.0.10" + "@chakra-ui/css-reset" "2.0.8" + "@chakra-ui/editable" "2.0.12" + "@chakra-ui/form-control" "2.0.11" + "@chakra-ui/hooks" "2.0.11" + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/image" "2.0.11" + "@chakra-ui/input" "2.0.11" + "@chakra-ui/layout" "2.1.8" + "@chakra-ui/live-region" "2.0.10" + "@chakra-ui/media-query" "3.2.7" + "@chakra-ui/menu" "2.1.1" + "@chakra-ui/modal" "2.2.1" + "@chakra-ui/number-input" "2.0.12" + "@chakra-ui/pin-input" "2.0.14" + "@chakra-ui/popover" "2.1.1" + "@chakra-ui/popper" "3.0.8" + "@chakra-ui/portal" "2.0.10" + "@chakra-ui/progress" "2.0.11" + "@chakra-ui/provider" "2.0.19" + "@chakra-ui/radio" "2.0.12" + "@chakra-ui/react-env" "2.0.10" + "@chakra-ui/select" "2.0.12" + "@chakra-ui/skeleton" "2.0.17" + "@chakra-ui/slider" "2.0.12" + "@chakra-ui/spinner" "2.0.10" + "@chakra-ui/stat" "2.0.11" + "@chakra-ui/styled-system" "2.3.4" + "@chakra-ui/switch" "2.0.13" + "@chakra-ui/system" "2.2.12" + "@chakra-ui/table" "2.0.11" + "@chakra-ui/tabs" "2.1.3" + "@chakra-ui/tag" "2.0.11" + "@chakra-ui/textarea" "2.0.12" + "@chakra-ui/theme" "2.1.13" + "@chakra-ui/toast" "3.0.13" + "@chakra-ui/tooltip" "2.2.0" + "@chakra-ui/transition" "2.0.10" + "@chakra-ui/utils" "2.0.11" + "@chakra-ui/visually-hidden" "2.0.11" + +"@chakra-ui/select@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/select/-/select-2.0.12.tgz#9b485e6a28c9aa468bc1c0d8a78aabd985b0c370" + integrity sha512-NCDMb0w48GYCHmazVSQ7/ysEpbnri+Up6n+v7yytf6g43TPRkikvK5CsVgLnAEj0lIdCJhWXTcZer5wG5KOEgA== + dependencies: + "@chakra-ui/form-control" "2.0.11" + +"@chakra-ui/shared-utils@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@chakra-ui/shared-utils/-/shared-utils-2.0.2.tgz#1df08133194c12ac4df9302604ec37784c2bb026" + integrity sha512-wC58Fh6wCnFFQyiebVZ0NI7PFW9+Vch0QE6qN7iR+bLseOzQY9miYuzPJ1kMYiFd6QTOmPJkI39M3wHqrPYiOg== + +"@chakra-ui/skeleton@2.0.17": + version "2.0.17" + resolved "https://registry.yarnpkg.com/@chakra-ui/skeleton/-/skeleton-2.0.17.tgz#737e08f771980f5b73060dc6c940691e7759d044" + integrity sha512-dL7viXEKDEzmAJGbHMj+QbGl9PAd0VWztEcWcz5wOGfmAcJllA0lVh6NmG/yqLb6iXPCX4Y1Y0Yurm459TEYWg== + dependencies: + "@chakra-ui/media-query" "3.2.7" + "@chakra-ui/react-use-previous" "2.0.2" + +"@chakra-ui/slider@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/slider/-/slider-2.0.12.tgz#42fc5fe385c507276da29f4aa49a6408ee853978" + integrity sha512-Cna04J7e4+F3tJNb7tRNfPP+koicbDsKJBp+f1NpR32JbRzIfrf2Vdr4hfD5/uOfC4RGxnVInNZzZLGBelLtLw== + dependencies: + "@chakra-ui/number-utils" "2.0.4" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-callback-ref" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-latest-ref" "2.0.2" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-pan-event" "2.0.5" + "@chakra-ui/react-use-size" "2.0.4" + "@chakra-ui/react-use-update-effect" "2.0.4" + +"@chakra-ui/spinner@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/spinner/-/spinner-2.0.10.tgz#f8b1b6f1c8f45e3aeab44d5ab1f1debc71e52573" + integrity sha512-SwId1xPaaFAaEYrR9eHkQHAuB66CbxwjWaQonEjeEUSh9ecxkd5WbXlsQSyf2hVRIqXJg0m3HIYblcKUsQt9Rw== + +"@chakra-ui/stat@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/stat/-/stat-2.0.11.tgz#0c052aee68486a892e09e802bb569dc984e31eae" + integrity sha512-ZPFK2fKufDSHD8bp/KhO3jLgW/b3PzdG4zV+7iTO7OYjxm5pkBfBAeMqfXGx4cl51rtWUKzsY0HV4vLLjcSjHw== + dependencies: + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/styled-system@2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@chakra-ui/styled-system/-/styled-system-2.3.4.tgz#6022c5a675b54a69b1d3c2d3e60258901dc7b82a" + integrity sha512-Lozbedu+GBj4EbHB/eGv475SFDLApsIEN9gNKiZJBJAE1HIhHn3Seh1iZQSrHC/Beq+D5cQq3Z+yPn3bXtFU7w== + dependencies: + csstype "^3.0.11" + lodash.mergewith "4.6.2" + +"@chakra-ui/switch@2.0.13": + version "2.0.13" + resolved "https://registry.yarnpkg.com/@chakra-ui/switch/-/switch-2.0.13.tgz#1736a501daaeedab295b166c176cd2fd990adac3" + integrity sha512-Ikj0L+SLLs/SnfGCiUChaldLIr/aizA1Q9D5+X6LtxpBnixFK/+fNXU+3juPDi9G1IFuNz2IAG51souO7C4nSQ== + dependencies: + "@chakra-ui/checkbox" "2.2.1" + +"@chakra-ui/system@2.2.12": + version "2.2.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/system/-/system-2.2.12.tgz#6792afebbebb21e03a4cbf6c0de79739f230acfc" + integrity sha512-I7hFQswp8tG6ogjEMFs5TsCItdCYuNxpLAULgUrLdOlsQeNnHNQhlL4zpIfD+VzguhsNy5lisbegAMKjdghTYg== + dependencies: + "@chakra-ui/color-mode" "2.1.9" + "@chakra-ui/react-utils" "2.0.8" + "@chakra-ui/styled-system" "2.3.4" + "@chakra-ui/utils" "2.0.11" + react-fast-compare "3.2.0" + +"@chakra-ui/table@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/table/-/table-2.0.11.tgz#9bd25d5383c94982b89e792675bc1d1f667f81f3" + integrity sha512-zQTiqPKEgjdeO/PG0FByn0fH4sPF7dLJF+YszrIzDc6wvpD96iY6MYLeV+CSelbH1g0/uibcJ10PSaFStfGUZg== + dependencies: + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/tabs@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@chakra-ui/tabs/-/tabs-2.1.3.tgz#17f462c7798292142a78938ae2e66781fe79c6da" + integrity sha512-9gUcj49LMt5QQnfJHR/ctr9VPttJ97CtQWuH2Irjb3RXkq1TRrz6wjythPImNQUv1/DYyXp2jsUhoEQc4Oz14Q== + dependencies: + "@chakra-ui/clickable" "2.0.10" + "@chakra-ui/descendant" "3.0.10" + "@chakra-ui/lazy-utils" "2.0.2" + "@chakra-ui/react-children-utils" "2.0.2" + "@chakra-ui/react-context" "2.0.4" + "@chakra-ui/react-use-controllable-state" "2.0.5" + "@chakra-ui/react-use-merge-refs" "2.0.4" + "@chakra-ui/react-use-safe-layout-effect" "2.0.2" + +"@chakra-ui/tag@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/tag/-/tag-2.0.11.tgz#14702adf5d1456dbbb84ea7a4b314953b92c323f" + integrity sha512-iJJcX+4hl+6Se/8eCRzG+xxDwZfiYgc4Ly/8s93M0uW2GLb+ybbfSE2DjeKSyk3mQVeGzuxGkBfDHH2c2v26ew== + dependencies: + "@chakra-ui/icon" "3.0.11" + "@chakra-ui/react-context" "2.0.4" + +"@chakra-ui/textarea@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/textarea/-/textarea-2.0.12.tgz#469c1d64cb855b3b534dcd7fcc1d927e60da8da1" + integrity sha512-msR9YMynRXwZIqR6DgjQ2MogA/cW1syBx/R0v3es+9Zx8zlbuKdoLhYqajHteCup8dUzTeIH2Vs2vAwgq4wu5A== + dependencies: + "@chakra-ui/form-control" "2.0.11" + +"@chakra-ui/theme-tools@2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@chakra-ui/theme-tools/-/theme-tools-2.0.12.tgz#b29d9fb626d35e3b00f532c64f95ea261d8f6997" + integrity sha512-mnMlKSmXkCjHUJsKWmJbgBTGF2vnLaMLv1ihkBn5eQcCubMQrBLTiMAEFl5pZdzuHItU6QdnLGA10smcXbNl0g== + dependencies: + "@chakra-ui/anatomy" "2.0.7" + "@ctrl/tinycolor" "^3.4.0" + +"@chakra-ui/theme@2.1.13": + version "2.1.13" + resolved "https://registry.yarnpkg.com/@chakra-ui/theme/-/theme-2.1.13.tgz#51e76179048cf87c0cb551a619a49d183246550e" + integrity sha512-qbrrvn9JstyLFV2qyhwgnhwzVs4zSJ4PcS3ScL8kafXJazTRU6onbCcjEZ5mVCw6z8uEz3jcE8Y5KIhVzaB+Xw== + dependencies: + "@chakra-ui/anatomy" "2.0.7" + "@chakra-ui/theme-tools" "2.0.12" + +"@chakra-ui/toast@3.0.13": + version "3.0.13" + resolved "https://registry.yarnpkg.com/@chakra-ui/toast/-/toast-3.0.13.tgz#e23f6a9c6cc373a78f66f465625b9777d8277033" + integrity sha512-5GADso5l5Tv1PAL1iocEneLs6U7I8HHMHSMvWdPFSmmJJh0XCH3fboh0C9LiFNIcnEGJmn+A5yGc4vjedA0h2A== + dependencies: + "@chakra-ui/alert" "2.0.11" + "@chakra-ui/close-button" "2.0.11" + "@chakra-ui/portal" "2.0.10" + "@chakra-ui/react-use-timeout" "2.0.2" + "@chakra-ui/react-use-update-effect" "2.0.4" + "@chakra-ui/styled-system" "2.3.4" + "@chakra-ui/theme" "2.1.13" + +"@chakra-ui/tooltip@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@chakra-ui/tooltip/-/tooltip-2.2.0.tgz#24e005f831cddf1c0e41dd246ed2771a97b8637c" + integrity sha512-oB97aQJBW+U3rRIt1ct7NaDRMnbW16JQ5ZBCl3BzN1VJWO3djiNuscpjVdZSceb+FdGSFo+GoDozp1ZwqdfFeQ== + dependencies: + "@chakra-ui/popper" "3.0.8" + "@chakra-ui/portal" "2.0.10" + "@chakra-ui/react-types" "2.0.3" + "@chakra-ui/react-use-disclosure" "2.0.5" + "@chakra-ui/react-use-event-listener" "2.0.4" + "@chakra-ui/react-use-merge-refs" "2.0.4" + +"@chakra-ui/transition@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@chakra-ui/transition/-/transition-2.0.10.tgz#a1ab786f4c2d0e41eb02dfd0f2f8f48a0bc9e35b" + integrity sha512-Tkkne8pIIY7f95TKt2aH+IAuQqzHmEt+ICPqvg74QbmIpKE5ptX0cd+P3swBANw4ACnJcCc2vWIaKmVBQ9clLw== + +"@chakra-ui/utils@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/utils/-/utils-2.0.11.tgz#8e773f900a8356bd10c48b59151a781dba1c7b70" + integrity sha512-4ZQdK6tbOuTrUCsAQBHWo7tw5/Q6pBV93ZbVpats61cSWMFGv32AIQw9/hA4un2zDeSWN9ZMVLNjAY2Dq/KQOA== + dependencies: + "@types/lodash.mergewith" "4.6.6" + css-box-model "1.2.1" + framesync "5.3.0" + lodash.mergewith "4.6.2" + +"@chakra-ui/visually-hidden@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@chakra-ui/visually-hidden/-/visually-hidden-2.0.11.tgz#b2eb236e803451b39cdfcce3c5ab52e773c066a3" + integrity sha512-e+5amYvnsmEQdiWH4XMyvrtGTdwz//+48vwj5CsNWWcselzkwqodmciy5rIrT71/SCQDOtmgnL7ZWAUOffxfsQ== + "@changesets/apply-release-plan@^6.1.3": version "6.1.3" resolved "https://registry.yarnpkg.com/@changesets/apply-release-plan/-/apply-release-plan-6.1.3.tgz#3bcc0bd57ba00d50d20df7d0141f1a9b2134eaf7" @@ -721,15 +1701,180 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@esbuild/android-arm@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.12.tgz#e548b10a5e55b9e10537a049ebf0bc72c453b769" - integrity sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA== +"@ctrl/tinycolor@^3.4.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32" + integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw== + +"@devicefarmer/adbkit-logcat@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@devicefarmer/adbkit-logcat/-/adbkit-logcat-1.1.0.tgz#866d3406dc9f3791446adfe3ae622ffc48607db4" + integrity sha512-K90P5gUXM/w+yzLvJIRQ+tJooNU6ipUPPQkljtPJ0laR66TGtpt4Gqsjm0n9dPHK1W5KGgU1R5wnCd6RTSlPNA== + +"@devicefarmer/adbkit-monkey@~1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@devicefarmer/adbkit-monkey/-/adbkit-monkey-1.0.1.tgz#7d225e5fdbdad8d6772453390ecab3f1b0eb72b1" + integrity sha512-HilPrVrCosYWqSyjfpDtaaN1kJwdlBpS+IAflP3z+e7nsEgk3JGJf1Vg0NgHJooTf5HDfXSyZqMVg+5jvXCK0g== + dependencies: + async "~0.2.9" + +"@devicefarmer/adbkit@2.11.3": + version "2.11.3" + resolved "https://registry.yarnpkg.com/@devicefarmer/adbkit/-/adbkit-2.11.3.tgz#0ad981a20aada3e4eff4871218f633c85cf7f2db" + integrity sha512-rsgWREAvSRQjdP9/3GoAV6Tq+o97haywgbTfCgt5yUqiDpaaq3hlH9FTo9XsdG8x+Jd0VQ9nTC2IXsDu8JGRSA== + dependencies: + "@devicefarmer/adbkit-logcat" "^1.1.0" + "@devicefarmer/adbkit-monkey" "~1.0.1" + bluebird "~2.9.24" + commander "^2.3.0" + debug "~2.6.3" + node-forge "^0.10.0" + split "~0.3.3" + +"@emotion/babel-plugin@^11.10.0": + version "11.10.2" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7" + integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.17.12" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/serialize" "^1.1.0" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.0.13" + +"@emotion/cache@^11.10.0": + version "11.10.3" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" + integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== + dependencies: + "@emotion/memoize" "^0.8.0" + "@emotion/sheet" "^1.2.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + stylis "4.0.13" + +"@emotion/hash@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" + integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== + +"@emotion/is-prop-valid@^0.8.2": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/is-prop-valid@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" + integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== + dependencies: + "@emotion/memoize" "^0.8.0" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/memoize@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" + integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== + +"@emotion/react@^11.10.4": + version "11.10.4" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199" + integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.0" + "@emotion/cache" "^11.10.0" + "@emotion/serialize" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8" + integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA== + dependencies: + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/unitless" "^0.8.0" + "@emotion/utils" "^1.2.0" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" + integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== + +"@emotion/styled@^11.10.4": + version "11.10.4" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.4.tgz#e93f84a4d54003c2acbde178c3f97b421fce1cd4" + integrity sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.0" + "@emotion/is-prop-valid" "^1.2.0" + "@emotion/serialize" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" + +"@emotion/unitless@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" + integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" + integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== + +"@emotion/utils@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" + integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== -"@esbuild/linux-loong64@0.15.12": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz#475b33a2631a3d8ca8aa95ee127f9a61d95bf9c1" - integrity sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw== +"@emotion/weak-memoize@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" + integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== + +"@esbuild/android-arm@0.15.11": + version "0.15.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.11.tgz#bdd9c3e098183bdca97075aa4c3e0152ed3e10ee" + integrity sha512-PzMcQLazLBkwDEkrNPi9AbjFt6+3I7HKbiYF2XtWQ7wItrHvEOeO3T8Am434zAozWtVP7lrTue1bEfc2nYWeCA== + +"@esbuild/linux-loong64@0.15.11": + version "0.15.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.11.tgz#2f4f9a1083dcb4fc65233b6f59003c406abf32e5" + integrity sha512-geWp637tUhNmhL3Xgy4Bj703yXB9dqiLJe05lCUfjSFDrQf9C/8pArusyPUbUbPwlC/EAUjBw32sxuIl/11dZw== + +"@eslint/eslintrc@^1.2.1": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" "@eslint/eslintrc@^1.2.3": version "1.2.3" @@ -1133,7 +2278,15 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.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.0", "@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== @@ -1147,7 +2300,7 @@ 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.1": +"@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== @@ -1874,6 +3027,11 @@ globby "^11.0.0" read-yaml-file "^1.1.0" +"@mdn/browser-compat-data@4.1.12": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-4.1.12.tgz#d266138c24295440619ea562ce3f4ec22600e897" + integrity sha512-y3Ntio6hb5+m6asxcA3nnIN6URjAFMji2EZZVYGd2Ag5On4mmvPhMnXdiIScCMXgHjFX+5qXuKaojLLhJHZPAg== + "@mdn/browser-compat-data@^3.3.14": version "3.3.14" resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-3.3.14.tgz#b72a37c654e598f9ae6f8335faaee182bebc6b28" @@ -1884,22 +3042,22 @@ resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-4.2.1.tgz#1fead437f3957ceebe2e8c3f46beccdb9bc575b8" integrity sha512-EWUguj2kd7ldmrF9F+vI5hUOralPd+sdsUnYbRy33vZTuZkduC1shE9TtEMEjAQwyfyMb4ole5KtjF8MsnQOlA== -"@microsoft/api-extractor-model@7.25.1": - version "7.25.1" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.25.1.tgz#be065a816cc83a5aa1b1203a3ac653dd68d62a0d" - integrity sha512-AaZ0ohCGLRjWiZviM+0p/DaxgMhbawS183LW2+CSqyEBh6wZks7NjoyhzhibAYapS4omnrmv96+0V/2wBvnIZQ== +"@microsoft/api-extractor-model@7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.25.2.tgz#a3e69e952122bbe3f0fc339a8ce0d9d20dd06406" + integrity sha512-+h1uCrLQXFAKMUdghhdDcnniDB+6UA/lS9ArlB4QZQ34UbLuXNy2oQ6fafFK8cKXU4mUPTF/yGRjv7JKD5L7eg== dependencies: - "@microsoft/tsdoc" "0.14.1" + "@microsoft/tsdoc" "0.14.2" "@microsoft/tsdoc-config" "~0.16.1" "@rushstack/node-core-library" "3.53.2" "@microsoft/api-extractor@^7.33.1": - version "7.33.4" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.33.4.tgz#645e7dbe2009016a784e96a01ad5c7776f08cd50" - integrity sha512-uZG4CHxVcQNpXBC77GwHaKFwGI9vAnzORY4fFN5JuTnQQDKS0vi4BazP4pmYYwbb8IdH4ocQSwOA3j9Ul/sWmg== + version "7.33.5" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.33.5.tgz#6f6791aa0b30fe1581002912ebd4565ef8313323" + integrity sha512-ENoWpTWarKNuodpRFDQr3jyBigHuv98KuJ8H5qXc1LZ1aP5Mk77lCo88HbPisTmSnGevJJHTScfd/DPznOb4CQ== dependencies: - "@microsoft/api-extractor-model" "7.25.1" - "@microsoft/tsdoc" "0.14.1" + "@microsoft/api-extractor-model" "7.25.2" + "@microsoft/tsdoc" "0.14.2" "@microsoft/tsdoc-config" "~0.16.1" "@rushstack/node-core-library" "3.53.2" "@rushstack/rig-package" "0.3.17" @@ -1961,6 +3119,59 @@ semver-match "0.1.1" upath "^2.0.1" +"@motionone/animation@^10.13.1": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.14.0.tgz#2f2a3517183bb58d82e389aac777fe0850079de6" + integrity sha512-h+1sdyBP8vbxEBW5gPFDnj+m2DCqdlAuf2g6Iafb1lcMnqjsRXWlPw1AXgvUMXmreyhqmPbJqoNfIKdytampRQ== + dependencies: + "@motionone/easing" "^10.14.0" + "@motionone/types" "^10.14.0" + "@motionone/utils" "^10.14.0" + tslib "^2.3.1" + +"@motionone/dom@10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.13.1.tgz#fc29ea5d12538f21b211b3168e502cfc07a24882" + integrity sha512-zjfX+AGMIt/fIqd/SL1Lj93S6AiJsEA3oc5M9VkUr+Gz+juRmYN1vfvZd6MvEkSqEjwPQgcjN7rGZHrDB9APfQ== + dependencies: + "@motionone/animation" "^10.13.1" + "@motionone/generators" "^10.13.1" + "@motionone/types" "^10.13.0" + "@motionone/utils" "^10.13.1" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.14.0.tgz#d8154b7f71491414f3cdee23bd3838d763fffd00" + integrity sha512-2vUBdH9uWTlRbuErhcsMmt1jvMTTqvGmn9fHq8FleFDXBlHFs5jZzHJT9iw+4kR1h6a4SZQuCf72b9ji92qNYA== + dependencies: + "@motionone/utils" "^10.14.0" + tslib "^2.3.1" + +"@motionone/generators@^10.13.1": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.14.0.tgz#e05d9dd56da78a4b92db99185848a0f3db62242d" + integrity sha512-6kRHezoFfIjFN7pPpaxmkdZXD36tQNcyJe3nwVqwJ+ZfC0e3rFmszR8kp9DEVFs9QL/akWjuGPSLBI1tvz+Vjg== + dependencies: + "@motionone/types" "^10.14.0" + "@motionone/utils" "^10.14.0" + tslib "^2.3.1" + +"@motionone/types@^10.13.0", "@motionone/types@^10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.14.0.tgz#148c34f3270b175397e49c3058b33fab405c21e3" + integrity sha512-3bNWyYBHtVd27KncnJLhksMFQ5o2MSdk1cA/IZqsHtA9DnRM1SYgN01CTcJ8Iw8pCXF5Ocp34tyAjY7WRpOJJQ== + +"@motionone/utils@^10.13.1", "@motionone/utils@^10.14.0": + version "10.14.0" + resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.14.0.tgz#a19a3464ed35b08506747b062d035c7bc9bbe708" + integrity sha512-sLWBLPzRqkxmOTRzSaD3LFQXCPHvDzyHJ1a3VP9PRzBxyVd2pv51/gMOsdAcxQ9n+MIeGJnxzXBYplUHKj4jkw== + dependencies: + "@motionone/types" "^10.14.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -2151,6 +3362,16 @@ resolved "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz" integrity sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw== +"@popperjs/core@^2.9.3": + version "2.11.6" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" + integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== + +"@remix-run/router@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.2.tgz#1c17eadb2fa77f80a796ad5ea9bf108e6993ef06" + integrity sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ== + "@rollup/plugin-commonjs@^20.0.0": version "20.0.0" resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz" @@ -2266,6 +3487,11 @@ colors "~1.2.1" string-argv "~0.3.1" +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -2280,6 +3506,25 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@tanstack/react-table@^8.5.22": + version "8.5.22" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.5.22.tgz#8a5250ac6437ca7c16e683cde65a065c0f573e24" + integrity sha512-01XaxQTxcI+w7g+i/YRblxdAikM9G7L1S7LwqaiD/gkKmNUs+g2BXw4GfIcuzzE75Q5t0BXlyqM7MyxxBuvQXA== + dependencies: + "@tanstack/table-core" "8.5.22" + +"@tanstack/table-core@8.5.22": + version "8.5.22" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.5.22.tgz#ba381db5f7f47558aba6e75cc94ee32a691defeb" + integrity sha512-D3wDbVXl3Bi5PdGfle6DijhLzZxrvMyZsE1dSHH0xBsqEbu7Pkxn5EEd6CA9tGsCgXIEP1s4Yfy1cZ9xnMz1sQ== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" @@ -2480,6 +3725,18 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/lodash.mergewith@4.6.6": + version "4.6.6" + resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10" + integrity sha512-RY/8IaVENjG19rxTZu9Nukqh0W2UrYgmBj5sdns4hWRZaV8PqR7wIKHFKzvOTjo4zVRV7sVI+yFhAJql12Kfqg== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.186" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.186.tgz#862e5514dd7bd66ada6c70ee5fce844b06c8ee97" + integrity sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw== + "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" @@ -2547,6 +3804,11 @@ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz" integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + "@types/pug@^2.0.4": version "2.0.5" resolved "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz" @@ -2571,6 +3833,22 @@ resolved "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== +"@types/react-dom@^18.0.6": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" + integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.0.21" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.21.tgz#b8209e9626bb00a34c76f55482697edd2b43cc67" + integrity sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" @@ -2585,6 +3863,11 @@ dependencies: "@types/node" "*" +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "@types/semver@^6.0.0": version "6.2.3" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.3.tgz#5798ecf1bec94eaa64db39ee52808ec0693315aa" @@ -2607,6 +3890,11 @@ resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz" integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== +"@types/webextension-polyfill@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@types/webextension-polyfill/-/webextension-polyfill-0.9.1.tgz#fcb5c352e2e461d0287774db89bc326b15b47844" + integrity sha512-6aNzPIhqKlAV9t06nwSH3/veAceYE2dS2RVFZI8V1+UXHqsFNB6cRwxNmheiBvEGRc45E/gyZNzH0xAYIC27KA== + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz" @@ -2619,7 +3907,7 @@ dependencies: "@types/yargs-parser" "*" -"@types/yauzl@^2.9.1": +"@types/yauzl@2.9.2", "@types/yauzl@^2.9.1": version "2.9.2" resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz" integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== @@ -2832,6 +4120,19 @@ "@typescript-eslint/types" "5.30.4" eslint-visitor-keys "^3.3.0" +"@vitejs/plugin-react@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-2.1.0.tgz#4c99df15e71d2630601bd3018093bdc787d40e55" + integrity sha512-am6rPyyU3LzUYne3Gd9oj9c4Rzbq5hQnuGXSMT6Gujq45Il/+bunwq3lrB7wghLkiF45ygMwft37vgJ/NE8IAA== + dependencies: + "@babel/core" "^7.18.13" + "@babel/plugin-transform-react-jsx" "^7.18.10" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-jsx-self" "^7.18.6" + "@babel/plugin-transform-react-jsx-source" "^7.18.6" + magic-string "^0.26.2" + react-refresh "^0.14.0" + "@xstate/fsm@^1.4.0": version "1.6.1" resolved "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.1.tgz" @@ -2846,6 +4147,16 @@ tslib "^2.3.1" upath2 "^3.1.12" +"@zag-js/element-size@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@zag-js/element-size/-/element-size-0.1.0.tgz#dfdb3f66a70328d0c3149aae29b8f99c10590c22" + integrity sha512-QF8wp0+V8++z+FHXiIw93+zudtubYszOtYbNgK39fg3pi+nCZtuSm4L1jC5QZMatNZ83MfOzyNCfgUubapagJQ== + +"@zag-js/focus-visible@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-0.1.0.tgz#9777bbaff8316d0b3a14a9095631e1494f69dbc7" + integrity sha512-PeaBcTmdZWcFf7n1aM+oiOdZc+sy14qi0emPIeUuGMTjbP0xLGrZu43kdpHnWSXy7/r4Ubp/vlg50MCV8+9Isg== + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" @@ -2864,13 +4175,13 @@ abbrev@1: resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" + mime-types "~2.1.24" + negotiator "0.6.2" acorn-globals@^6.0.0: version "6.0.0" @@ -2880,7 +4191,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== @@ -2905,11 +4216,75 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.7.0, acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= +addons-linter@4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/addons-linter/-/addons-linter-4.14.0.tgz#e8a5b99665a7e7df3fda2da42d73593fea8db3f9" + integrity sha512-TH3/PMS4Dd0Jf3kXW7DLXseHZcD7ZbnfnQAztkrP4YC0HQKQVZJ+lGOuDOGUtVQDMwC/eEdvHvZoRnHDer5qkg== + dependencies: + "@mdn/browser-compat-data" "4.1.12" + addons-moz-compare "1.2.0" + addons-scanner-utils "6.3.0" + ajv "6.12.6" + ajv-merge-patch "4.1.0" + chalk "4.1.2" + cheerio "1.0.0-rc.10" + columnify "1.6.0" + common-tags "1.8.2" + deepmerge "4.2.2" + eslint "8.11.0" + eslint-plugin-no-unsanitized "4.0.1" + eslint-visitor-keys "3.3.0" + espree "9.3.1" + esprima "4.0.1" + fluent-syntax "0.13.0" + glob "7.2.0" + image-size "1.0.1" + is-mergeable-object "1.1.1" + jed "1.1.1" + os-locale "5.0.0" + pino "7.9.1" + postcss "8.4.12" + relaxed-json "1.0.3" + semver "7.3.5" + sha.js "2.4.11" + source-map-support "0.5.21" + tosource "1.0.0" + upath "2.0.1" + yargs "17.4.0" + yauzl "2.10.0" + +addons-moz-compare@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/addons-moz-compare/-/addons-moz-compare-1.2.0.tgz#d6246cbae224ab629cb7da397b2b9602938ea085" + integrity sha512-COG8qk2/dubPqabfcoJW4E7pm2EQDI43iMrHnhlobvq/uRMEzx/PYJ1KaUZ97Vgg44R3QdRG5CvDsTRbMUHcDw== + +addons-scanner-utils@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/addons-scanner-utils/-/addons-scanner-utils-6.3.0.tgz#99596585a1e5d50f543e4f765cb3549bcf9d48db" + integrity sha512-sD4U7TX/NFDUYVheydrcpHH9xG3E0eVBFDn1RuUkGpqRyay3SsOU75Pl2lWAbCmeE0Mh9inU1Fwl7Mm1VRWkZw== + dependencies: + "@types/yauzl" "2.9.2" + common-tags "1.8.2" + first-chunk-stream "3.0.0" + strip-bom-stream "4.0.0" + upath "2.0.1" + yauzl "2.10.0" + +adm-zip@~0.5.x: + version "0.5.9" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.9.tgz#b33691028333821c0cf95c31374c5462f2905a83" + integrity sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -2934,7 +4309,15 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@~6.12.6: +ajv-merge-patch@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ajv-merge-patch/-/ajv-merge-patch-4.1.0.tgz#cd580e5860ac53431d6aa901fa3d5e2eb2b74a6c" + integrity sha512-0mAYXMSauA8RZ7r+B4+EAOYcZEcO9OK5EiQCR7W7Cv4E44pJj56ZnkKLJ9/PAcOc0dT+LlV9fdDcq2TxVJfOYw== + dependencies: + fast-json-patch "^2.0.6" + json-merge-patch "^0.2.3" + +ajv@6.12.6, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@~6.12.6: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2944,11 +4327,28 @@ 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.8.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-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@^4.1.1, ansi-colors@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" @@ -3017,6 +4417,11 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== +any-promise@^1.0.0, any-promise@^1.1.0, any-promise@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" @@ -3060,6 +4465,13 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-hidden@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.1.tgz#ad8c1edbde360b454eb2bf717ea02da00bfee0f8" + integrity sha512-PN344VAf9j1EAi+jyVHOJ8XidQdPVssGco39eNcsGdM4wcsILtxrKLkbuiMfLWYROK1FjRQasMWCBttrhjnr6A== + dependencies: + tslib "^2.0.0" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz" @@ -3079,8 +4491,8 @@ array-differ@^3.0.0: array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-ify@^1.0.0: version "1.0.0" @@ -3153,6 +4565,16 @@ ast-metadata-inferer@^0.7.0: dependencies: "@mdn/browser-compat-data" "^3.3.14" +async@^3.2.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +async@~0.2.9: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" @@ -3163,6 +4585,11 @@ at-least-node@^1.0.0: resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -3248,6 +4675,15 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + 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" @@ -3336,7 +4772,7 @@ better-path-resolve@1.0.0: big.js@^5.2.2: version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== binary-extensions@^2.0.0: @@ -3367,29 +4803,46 @@ bluebird@^3.5.2: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== +bluebird@~2.9.24: + version "2.9.34" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.9.34.tgz#2f7b4ec80216328a9fddebdf69c8d4942feff7d8" + integrity sha512-ZDzCb87X7/IP1uzQ5eJZB+WoQRGTnKL5DHWvPw6kkMbQseouiQIrEi3P1UGE0D1k0N5/+aP/5GMCyHZ1xYJyHQ== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: - bytes "3.1.2" + bytes "3.1.0" content-type "~1.0.4" debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" + depd "~1.1.2" + http-errors "1.7.2" iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -3416,7 +4869,7 @@ braces@^1.8.2: braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" @@ -3462,7 +4915,7 @@ browserslist@^4.16.6: node-releases "^1.1.77" picocolors "^0.2.1" -browserslist@^4.16.8, browserslist@^4.21.4: +browserslist@^4.16.8, browserslist@^4.21.3, browserslist@^4.21.4: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== @@ -3502,6 +4955,11 @@ buffer-crc32@~0.2.3: resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -3538,6 +4996,16 @@ builtins@^1.0.3: resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= +bunyan@1.8.15: + version "1.8.15" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" + integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== + optionalDependencies: + dtrace-provider "~0.8" + moment "^2.19.3" + mv "~2" + safe-json-stringify "~1" + byline@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" @@ -3548,10 +5016,10 @@ byte-size@^7.0.0: resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^15.0.5, cacache@^15.2.0: version "15.2.0" @@ -3576,6 +5044,19 @@ cacache@^15.0.5, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" @@ -3617,16 +5098,16 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" +camelcase@6.2.0, camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" @@ -3647,6 +5128,14 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" @@ -3658,9 +5147,9 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3675,14 +5164,6 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^5.0.0, chalk@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" @@ -3698,6 +5179,11 @@ chardet@^0.7.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz" @@ -3709,7 +5195,7 @@ cheerio-select@^1.5.0: domhandler "^4.2.0" domutils "^2.7.0" -cheerio@^1.0.0-rc.3: +cheerio@1.0.0-rc.10, cheerio@^1.0.0-rc.3: version "1.0.0-rc.10" resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz" integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== @@ -3747,6 +5233,16 @@ chownr@^2.0.0: resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +chrome-launcher@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.0.tgz#5144a57aba0cf2f4cbe61dccefdde024fb3ca7fc" + integrity sha512-ZQqX5kb9H0+jy1OqLnWampfocrtSZaGl7Ny3F9GRha85o4odbL8x55paUzh51UC7cEmZ5obp3H2Mm70uC2PpRA== + dependencies: + "@types/node" "*" + escape-string-regexp "^4.0.0" + is-wsl "^2.2.0" + lighthouse-logger "^1.0.0" + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" @@ -3777,6 +5273,11 @@ clean-stack@^2.0.0: resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" @@ -3842,6 +5343,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" @@ -3933,6 +5441,14 @@ colors@~1.2.1: resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== +columnify@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" + integrity sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q== + dependencies: + strip-ansi "^6.0.1" + wcwidth "^1.0.0" + columnify@^1.5.4: version "1.5.4" resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" @@ -3948,7 +5464,14 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.20.0, commander@^2.20.3: +commander@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + integrity sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A== + dependencies: + graceful-readlink ">= 1.0.0" + +commander@^2.20.0, commander@^2.20.3, commander@^2.3.0, commander@^2.6.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3968,6 +5491,11 @@ comment-json@^3.0.3: has-own-prop "^2.0.0" repeat-string "^1.6.1" +common-tags@1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" @@ -3986,11 +5514,26 @@ compare-versions@^4.1.3: resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== +compute-scroll-into-view@1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz#80e3ebb25d6aa89f42e533956cb4b16a04cfe759" + integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +concat-stream@^1.4.7: + version "1.6.2" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + concat-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" @@ -4030,6 +5573,18 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + console-clear@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz" @@ -4045,16 +5600,16 @@ construct-style-sheets-polyfill@^3.1.0: resolved "https://registry.yarnpkg.com/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.1.0.tgz#c490abd79efdb359fafa62ec14ea55232be0eecf" integrity sha512-HBLKP0chz8BAY6rBdzda11c3wAZeCZ+kIG4weVC2NM3AXzxx09nhe8t0SQNdloAvg5GLuHwq/0SPOOSPvtCcKw== -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: - safe-buffer "5.2.1" + safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== conventional-changelog-angular@^5.0.12: @@ -4147,15 +5702,32 @@ 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@^1.5.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-to-clipboard@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" + integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== + dependencies: + toggle-selection "^1.0.6" + +core-js@3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.0.tgz#f479dbfc3dffb035a0827602dd056839a774aa71" + integrity sha512-YUdI3fFu4TF/2WykQ2xzSiTQdldLB4KVuL9WeAy5XONZYt5Cun/fpQvctoKbCgvPhmzADeesTk/j2Rdx77AcKQ== core-js@^2.4.0: version "2.6.12" @@ -4210,6 +5782,13 @@ cross-env@^5.2.0: dependencies: cross-spawn "^6.0.5" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-fetch@3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" @@ -4237,7 +5816,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4246,6 +5825,23 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-box-model@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" + integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== + dependencies: + tiny-invariant "^1.0.6" + css-color-names@0.0.4, css-color-names@^0.0.4: version "0.0.4" resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz" @@ -4432,6 +6028,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +csstype@^3.0.11, csstype@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== + csv-generate@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" @@ -4493,27 +6094,32 @@ dateformat@^3.0.0: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2.6.9: +debounce@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" + integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== + +debug@2.6.9, debug@^2.6.9, debug@~2.6.3: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "2.0.0" - -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, 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== - dependencies: - ms "2.1.2" + ms "2.0.0" -debug@4.3.2: +debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" +debug@4.3.4, 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== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" @@ -4527,6 +6133,11 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" +decamelize@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.0.tgz#88358157b010ef133febfd27c18994bd80c6215b" + integrity sha512-U75DcT5hrio3KNtvdULAWnLiAPbFUC4191ldxMmj4FA/mRuBnmDwU0boNfPyFRhnan+Jm+haLeSn3P0afcBn4w== + decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" @@ -4538,15 +6149,34 @@ decimal.js@^10.2.1: integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.0" dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-equal@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -4557,7 +6187,14 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^4.2.2: +deepcopy@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/deepcopy/-/deepcopy-2.1.0.tgz#2deb0dd52d079c2ecb7924b640a7c3abd4db1d6d" + integrity sha512-8cZeTb1ZKC3bdSCP6XOM1IsTczIO73fdqtwa2B0N15eAz7gmyhQo+mc5gnFuulsgN3vIQYmTgbmQVKalH1dKvQ== + dependencies: + type-detect "^4.0.8" + +deepmerge@4.2.2, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== @@ -4569,6 +6206,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" @@ -4577,9 +6219,9 @@ define-properties@^1.1.3: object-keys "^1.0.12" define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -4594,12 +6236,7 @@ delegates@^1.0.0: resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@^1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -4609,10 +6246,10 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^5.0.0: version "5.0.0" @@ -4629,6 +6266,11 @@ detect-newline@^3.0.0: resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node-es@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" + integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== + devtools-protocol@0.0.1036444: version "0.0.1036444" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1036444.tgz#a570d3cdde61527c82f9b03919847b8ac7b1c2b9" @@ -4763,11 +6405,33 @@ dotenv@^8.1.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +dtrace-provider@~0.8: + version "0.8.8" + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" + integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== + dependencies: + nan "^2.14.0" + +duplexer3@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + duplexer@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +duplexify@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" + integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -4781,10 +6445,17 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.723: version "1.3.784" @@ -4823,13 +6494,13 @@ emoji-regex@^9.2.2: emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.12: version "0.1.13" @@ -4838,7 +6509,7 @@ encoding@^0.1.12: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@~1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -5006,205 +6677,215 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es6-error@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +es6-promisify@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-7.0.0.tgz#9a710008dd6a4ab75a89e280bad787bfb749927b" + integrity sha512-ginqzK3J90Rd4/Yz7qRrqUeIpe3TwSXTPPZtPne7tGBPeAaQiU8qt4fpKApnxHcq1AwtUdHVg5P77x/yrggG8Q== + esbuild-android-64@0.14.38: version "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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz#5e8151d5f0a748c71a7fbea8cee844ccf008e6fc" - integrity sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q== +esbuild-android-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.11.tgz#50402129c3e85bb06434e212374c5f693e4c5f01" + integrity sha512-rrwoXEiuI1kaw4k475NJpexs8GfJqQUKcD08VR8sKHmuW9RUuTR2VxcupVvHdiGh9ihxL9m3lpqB1kju92Ialw== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz#5ee72a6baa444bc96ffcb472a3ba4aba2cc80666" - integrity sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA== +esbuild-android-arm64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.11.tgz#49bee35218ea2ccf1a0c5f187af77c1c0a5dee71" + integrity sha512-/hDubOg7BHOhUUsT8KUIU7GfZm5bihqssvqK5PfO4apag7YuObZRZSzViyEKcFn2tPeHx7RKbSBXvAopSHDZJQ== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz#70047007e093fa1b3ba7ef86f9b3fa63db51fe25" - integrity sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q== +esbuild-darwin-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.11.tgz#89a90c8cf6f0029ac4169bfedd012a0412c1575f" + integrity sha512-1DqHD0ms3AhiwkKnjRUzmiW7JnaJJr5FKrPiR7xuyMwnjDqvNWDdMq4rKSD9OC0piFNK6n0LghsglNMe2MwJtA== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz#41c951f23d9a70539bcca552bae6e5196696ae04" - integrity sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw== +esbuild-darwin-arm64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.11.tgz#556f4385c6de806cc81132dd7b8af00fe9d292df" + integrity sha512-OMzhxSbS0lwwrW40HHjRCeVIJTURdXFA8c3GU30MlHKuPCcvWNUIKVucVBtNpJySXmbkQMDJdJNrXzNDyvoqvQ== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz#a761b5afd12bbedb7d56c612e9cfa4d2711f33f0" - integrity sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw== +esbuild-freebsd-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.11.tgz#fd86fd1b3b65366048f35b996d9cdf3547384eee" + integrity sha512-8dKP26r0/Qyez8nTCwpq60QbuYKOeBygdgOAWGCRalunyeqWRoSZj9TQjPDnTTI9joxd3QYw3UhVZTKxO9QdRg== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz#6b0839d4d58deabc6cbd96276eb8cbf94f7f335e" - integrity sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g== +esbuild-freebsd-arm64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.11.tgz#d346bcacfe9779ebc1a11edac1bdedeff6dda3b1" + integrity sha512-aSGiODiukLGGnSg/O9+cGO2QxEacrdCtCawehkWYTt5VX1ni2b9KoxpHCT9h9Y6wGqNHmXFnB47RRJ8BIqZgmQ== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz#bd50bfe22514d434d97d5150977496e2631345b4" - integrity sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA== +esbuild-linux-32@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.11.tgz#64b50e774bf75af7dcc6a73ad509f2eb0ac4487b" + integrity sha512-lsrAfdyJBGx+6aHIQmgqUonEzKYeBnyfJPkT6N2dOf1RoXYYV1BkWB6G02tjsrz1d5wZzaTc3cF+TKmuTo/ZwA== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz#074bb2b194bf658245f8490f29c01ffcdfa8c931" - integrity sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA== +esbuild-linux-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.11.tgz#fba3a78b95769772863f8f6dc316abca55cf8416" + integrity sha512-Y2Rh+PcyVhQqXKBTacPCltINN3uIw2xC+dsvLANJ1SpK5NJUtxv8+rqWpjmBgaNWKQT1/uGpMmA9olALy9PLVA== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz#3bf789c4396dc032875a122988efd6f3733f28f5" - integrity sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ== +esbuild-linux-arm64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.11.tgz#c0cb31980eee066bfd39a4593660a0ecebe926cb" + integrity sha512-uhcXiTwTmD4OpxJu3xC5TzAAw6Wzf9O1XGWL448EE9bqGjgV1j+oK3lIHAfsHnuIn8K4nDW8yjX0Sv5S++oRuw== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz#b91b5a8d470053f6c2c9c8a5e67ec10a71fe4a67" - integrity sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A== +esbuild-linux-arm@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.11.tgz#7824d20099977aa671016c7de7a5038c9870010f" + integrity sha512-TJllTVk5aSyqPFvvcHTvf6Wu1ZKhWpJ/qNmZO8LL/XeB+LXCclm7HQHNEIz6MT7IX8PmlC1BZYrOiw2sXSB95A== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz#2fb54099ada3c950a7536dfcba46172c61e580e2" - integrity sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A== +esbuild-linux-mips64le@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.11.tgz#10627331c90164e553429ed25e025184bba485b6" + integrity sha512-WD61y/R1M4BLe4gxXRypoQ0Ci+Vjf714QYzcPNkiYv5I8K8WDz2ZR8Bm6cqKxd6rD+e/rZgPDbhQ9PCf7TMHmA== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz#9e3b8c09825fb27886249dfb3142a750df29a1b7" - integrity sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg== +esbuild-linux-ppc64le@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.11.tgz#be42679a36a5246b893fc8b898135ebacb5a0a14" + integrity sha512-JVleZS9oPVLTlBhPTWgOwxFWU/wMUdlBwTbGA4GF8c38sLbS13cupj+C8bLq929jU7EMWry4SaL+tKGIaTlqKg== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz#923d0f5b6e12ee0d1fe116b08e4ae4478fe40693" - integrity sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA== +esbuild-linux-riscv64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.11.tgz#3ac2f328e3db73cbff833ada94314d8e79503e54" + integrity sha512-9aLIalZ2HFHIOZpmVU11sEAS9F8TnHw49daEjcgMpBXHFF57VuT9f9/9LKJhw781Gda0P9jDkuCWJ0tFbErvJw== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz#3b1620220482b96266a0c6d9d471d451a1eab86f" - integrity sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww== +esbuild-linux-s390x@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.11.tgz#e774e0df061b6847d86783bf3c8c4300a72e03ad" + integrity sha512-sZHtiXXOKsLI3XGBGoYO4qKBzJlb8xNsWmvFiwFMHFzA4AXgDP1KDp7Dawe9C2pavTRBDvl+Ok4n/DHQ59oaTg== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz#276730f80da646859b1af5a740e7802d8cd73e42" - integrity sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w== +esbuild-netbsd-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.11.tgz#55e265fa4489e3f396b16c81f6f5a11d6ca2a9a4" + integrity sha512-hUC9yN06K9sg7ju4Vgu9ChAPdsEgtcrcLfyNT5IKwKyfpLvKUwCMZSdF+gRD3WpyZelgTQfJ+pDx5XFbXTlB0A== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz#bd0eea1dd2ca0722ed489d88c26714034429f8ae" - integrity sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw== +esbuild-openbsd-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.11.tgz#bc04103ccfd8c2f2241e1add0b51a095955b73c4" + integrity sha512-0bBo9SQR4t66Wd91LGMAqmWorzO0TTzVjYiifwoFtel8luFeXuPThQnEm5ztN4g0fnvcp7AnUPPzS/Depf17wQ== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz#5e56bf9eef3b2d92360d6d29dcde7722acbecc9e" - integrity sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg== +esbuild-sunos-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.11.tgz#ccd580305d31fde07b5c386da79c942aaf069013" + integrity sha512-EuBdTGlsMTjEl1sQnBX2jfygy7iR6CKfvOzi+gEOfhDqbHXsmY1dcpbVtcwHAg9/2yUZSfMJHMAgf1z8M4yyyw== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz#a4f1a301c1a2fa7701fcd4b91ef9d2620cf293d0" - integrity sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw== +esbuild-windows-32@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.11.tgz#40fe1d48f9b20a76f6db5109aaaf1511aed58c71" + integrity sha512-O0/Wo1Wk6dc0rZSxkvGpmTNIycEznHmkObTFz2VHBhjPsO4ZpCgfGxNkCpz4AdAIeMczpTXt/8d5vdJNKEGC+Q== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz#bc2b467541744d653be4fe64eaa9b0dbbf8e07f6" - integrity sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA== +esbuild-windows-64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.11.tgz#80c58b1ef2ff030c78e3a06e7a922776cc4cb687" + integrity sha512-x977Q4HhNjnHx00b4XLAnTtj5vfbdEvkxaQwC1Zh5AN8g5EX+izgZ6e5QgqJgpzyRNJqh4hkgIJF1pyy1be0mQ== 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.12: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz#9a7266404334a86be800957eaee9aef94c3df328" - integrity sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA== +esbuild-windows-arm64@0.15.11: + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.11.tgz#018624023b5c3f0cca334cc99f5ef7134d396333" + integrity sha512-VwUHFACuBahrvntdcMKZteUZ9HaYrBRODoKe4tIWxguQRvvYoYb7iu5LrcRS/FQx8KPZNaa72zuqwVtHeXsITw== esbuild@^0.14.38: version "0.14.38" @@ -5233,42 +6914,47 @@ esbuild@^0.14.38: esbuild-windows-arm64 "0.14.38" esbuild@^0.15.9: - version "0.15.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.12.tgz#6c8e22d6d3b7430d165c33848298d3fc9a1f251c" - integrity sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng== + version "0.15.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.11.tgz#524d48612a9aa7edc1753c83459cb6fcae0cb66e" + integrity sha512-OgHGuhlfZ//mToxjte1D5iiiQgWfJ2GByVMwEC/IuoXsBGkuyK1+KrjYu0laSpnN/L1UmLUCv0s25vObdc1bVg== optionalDependencies: - "@esbuild/android-arm" "0.15.12" - "@esbuild/linux-loong64" "0.15.12" - esbuild-android-64 "0.15.12" - esbuild-android-arm64 "0.15.12" - esbuild-darwin-64 "0.15.12" - esbuild-darwin-arm64 "0.15.12" - esbuild-freebsd-64 "0.15.12" - esbuild-freebsd-arm64 "0.15.12" - esbuild-linux-32 "0.15.12" - esbuild-linux-64 "0.15.12" - esbuild-linux-arm "0.15.12" - esbuild-linux-arm64 "0.15.12" - esbuild-linux-mips64le "0.15.12" - esbuild-linux-ppc64le "0.15.12" - esbuild-linux-riscv64 "0.15.12" - esbuild-linux-s390x "0.15.12" - esbuild-netbsd-64 "0.15.12" - esbuild-openbsd-64 "0.15.12" - esbuild-sunos-64 "0.15.12" - esbuild-windows-32 "0.15.12" - esbuild-windows-64 "0.15.12" - esbuild-windows-arm64 "0.15.12" + "@esbuild/android-arm" "0.15.11" + "@esbuild/linux-loong64" "0.15.11" + esbuild-android-64 "0.15.11" + esbuild-android-arm64 "0.15.11" + esbuild-darwin-64 "0.15.11" + esbuild-darwin-arm64 "0.15.11" + esbuild-freebsd-64 "0.15.11" + esbuild-freebsd-arm64 "0.15.11" + esbuild-linux-32 "0.15.11" + esbuild-linux-64 "0.15.11" + esbuild-linux-arm "0.15.11" + esbuild-linux-arm64 "0.15.11" + esbuild-linux-mips64le "0.15.11" + esbuild-linux-ppc64le "0.15.11" + esbuild-linux-riscv64 "0.15.11" + esbuild-linux-s390x "0.15.11" + esbuild-netbsd-64 "0.15.11" + esbuild-openbsd-64 "0.15.11" + esbuild-sunos-64 "0.15.11" + esbuild-windows-32 "0.15.11" + esbuild-windows-64 "0.15.11" + esbuild-windows-arm64 "0.15.11" escalade@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" @@ -5328,6 +7014,11 @@ eslint-plugin-jest@^27.1.3: dependencies: "@typescript-eslint/utils" "^5.10.0" +eslint-plugin-no-unsanitized@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.1.tgz#e2343265467ba2270ade478cbe07bbafeaea412d" + integrity sha512-y/lAMWnPPC7RYuUdxlEL/XiCL8FehN9h9s3Kjqbp/Kv0i9NZs+IXSC2kS546Fa4Bumwy31HlVS/OdWX0Kxb5Xg== + eslint-plugin-svelte3@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3" @@ -5364,15 +7055,56 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" +eslint-visitor-keys@3.3.0, eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "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-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: - version "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@8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37" + integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA== + dependencies: + "@eslint/eslintrc" "^1.2.1" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.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 "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + 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" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" eslint@^8.15.0: version "8.15.0" @@ -5456,6 +7188,24 @@ eslint@^8.19.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +espree@9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + +espree@^9.3.1, espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + espree@^9.3.2: version "9.3.2" resolved "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz" @@ -5465,7 +7215,7 @@ espree@^9.3.2: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@4.0.1, esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5516,14 +7266,29 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + execa@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" @@ -5581,38 +7346,37 @@ expect@^27.5.1: jest-message-util "^27.5.1" express@^4.16.4: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + version "4.17.1" + resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: - accepts "~1.3.8" + accepts "~1.3.7" array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" + body-parser "1.19.0" + content-disposition "0.5.3" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.4.0" cookie-signature "1.0.6" debug "2.6.9" - depd "2.0.0" + depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "~1.1.2" fresh "0.5.2" - http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "2.4.1" + on-finished "~2.3.0" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" + proxy-addr "~2.0.5" + qs "6.7.0" range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -5629,7 +7393,7 @@ extendable-error@^0.1.5: external-editor@^3.0.3, external-editor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -5664,6 +7428,11 @@ extsprintf@^1.2.0: resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -5702,6 +7471,13 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-json-patch@^2.0.6: + version "2.2.1" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.2.1.tgz#18150d36c9ab65c7209e7d4eb113f4f8eaabe6d9" + integrity sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig== + dependencies: + fast-deep-equal "^2.0.1" + 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" @@ -5723,6 +7499,11 @@ fast-mhtml@^1.1.9: express "^4.16.4" filenamify "^2.1.0" +fast-redact@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" + integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== + fastparse@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz" @@ -5818,17 +7599,17 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "2.4.1" + on-finished "~2.3.0" parseurl "~1.3.3" - statuses "2.0.1" + statuses "~1.5.0" unpipe "~1.0.0" find-cache-dir@^3.3.2: @@ -5840,6 +7621,11 @@ find-cache-dir@^3.3.2: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" @@ -5871,6 +7657,22 @@ find-yarn-workspace-root2@1.2.16: micromatch "^4.0.2" pkg-dir "^4.2.0" +firefox-profile@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/firefox-profile/-/firefox-profile-4.2.2.tgz#1c7c78d103e2fe4102154e0300dbc17124847db5" + integrity sha512-3kI17Xl9dL9AeRkpV1yahsJ+UbekkPtlQswKrIsTY1NLgxtEOR4R19rjGGz5+7/rP8Jt6fvxHk+Bai9R6Eai3w== + dependencies: + adm-zip "~0.5.x" + fs-extra "~9.0.1" + ini "~2.0.0" + minimist "^1.2.5" + xml2js "~0.4.23" + +first-chunk-stream@3.0.0, first-chunk-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz#06972a66263505ed82b2c4db93c1b5e078a6576a" + integrity sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw== + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" @@ -5884,6 +7686,18 @@ flatted@^3.1.0: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz" integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg== +fluent-syntax@0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/fluent-syntax/-/fluent-syntax-0.13.0.tgz#417144d99cba94ff474c422b3e6623d5a842855a" + integrity sha512-0Bk1AsliuYB550zr4JV9AYhsETsD3ELXUQzdXGJfIc1Ni/ukAfBdQInDhVMYJUaT2QxoamNslwkYF7MlOrPUwg== + +focus-lock@^0.11.2: + version "0.11.3" + resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.11.3.tgz#c094e8f109d780f56038abdeec79328fd56b627f" + integrity sha512-4n0pYcPTa/uI7Q66BZna61nRT7lDhnuJ9PJr6wiDjx4uStg491ks41y7uOG+s0umaaa+hulNKSldU9aTg9/yVg== + dependencies: + tslib "^2.0.3" + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -5928,19 +7742,57 @@ form-data@~2.3.2: forwarded@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +framer-motion@^7.3.6: + version "7.5.3" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-7.5.3.tgz#a1de7d6c4abbf7333619d4a6c8df74c12240be43" + integrity sha512-VvANga9Z7bYtKMAsM/je81FwJDHfThOYywN04xVQ4OGdMVY09Bowx/q7nZd6XtytLuv6byc6GT1mYwag+SQ/nw== + dependencies: + "@motionone/dom" "10.13.1" + framesync "6.1.2" + hey-listen "^1.0.8" + popmotion "11.0.5" + style-value-types "5.1.2" + tslib "2.4.0" + optionalDependencies: + "@emotion/is-prop-valid" "^0.8.2" + +framesync@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b" + integrity sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA== + dependencies: + tslib "^2.1.0" + +framesync@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.1.2.tgz#755eff2fb5b8f3b4d2b266dd18121b300aefea27" + integrity sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g== + dependencies: + tslib "2.4.0" + fresh@0.5.2: version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@9.1.0, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^10.0.0: version "10.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz" @@ -5977,15 +7829,15 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +fs-extra@~9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc" + integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ== dependencies: at-least-node "^1.0.0" graceful-fs "^4.2.0" jsonfile "^6.0.1" - universalify "^2.0.0" + universalify "^1.0.0" fs-minipass@^1.2.7: version "1.2.7" @@ -6036,6 +7888,18 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +fx-runner@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fx-runner/-/fx-runner-1.2.0.tgz#6fd6d0fc47b5c32c176aa22af2182227664a03fe" + integrity sha512-/zR9BmHF8h4OaVJ+fHHJBv/5FdPV9mjOAPIscQZbAijm7Aa15Ls/P8UBHD5OKU5jwu2niTxkkzzHKITE7oCMoQ== + dependencies: + commander "2.9.0" + shell-quote "1.7.3" + spawn-sync "1.0.15" + when "3.7.7" + which "1.2.4" + winreg "0.0.12" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" @@ -6068,9 +7932,9 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -6085,7 +7949,7 @@ get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-intrinsic@^1.1.3: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== @@ -6094,6 +7958,11 @@ get-intrinsic@^1.1.3: has "^1.0.3" has-symbols "^1.0.3" +get-nonce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" + integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" @@ -6129,7 +7998,14 @@ get-stdin@~9.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== -get-stream@^5.1.0: +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== @@ -6234,6 +8110,34 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A== + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" @@ -6258,6 +8162,13 @@ glob@~7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" @@ -6324,6 +8235,23 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.2.6" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" @@ -6344,11 +8272,21 @@ graceful-fs@^4.2.9: resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w== + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== + handlebars@^4.7.6: version "4.7.7" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" @@ -6455,6 +8393,11 @@ has-unicode@^2.0.0, has-unicode@^2.0.1: resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + has@^1.0.0, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -6467,6 +8410,18 @@ hex-color-regex@^1.1.0: resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" @@ -6511,21 +8466,32 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: - depd "2.0.0" + depd "~1.1.2" inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" http-proxy-agent@^4.0.1: version "4.0.1" @@ -6566,6 +8532,11 @@ human-id@^1.0.2: resolved "https://registry.yarnpkg.com/human-id/-/human-id-1.0.2.tgz#e654d4b2b0d8b07e45da9f6020d8af17ec0a5df3" integrity sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" @@ -6597,6 +8568,11 @@ icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: resolved "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= +idb@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + identity-obj-proxy@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" @@ -6631,6 +8607,18 @@ ignore@^5.1.8, ignore@^5.2.0, ignore@~5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +image-size@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.1.tgz#86d6cfc2b1d19eab5d2b368d4b9194d9e48541c5" + integrity sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ== + dependencies: + queue "6.0.2" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz" @@ -6645,6 +8633,14 @@ import-cwd@^3.0.0: dependencies: import-from "^3.0.0" +import-fresh@3.3.0, import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" @@ -6653,14 +8649,6 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - import-from@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz" @@ -6675,6 +8663,11 @@ import-from@^3.0.0: dependencies: resolve-from "^5.0.0" +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== + import-lazy@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" @@ -6716,21 +8709,26 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.2, ini@^1.3.4: - version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@~2.0.0: +ini@2.0.0, ini@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== +ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + init-package-json@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.3.tgz" @@ -6795,14 +8793,26 @@ internal-slot@^1.0.3: side-channel "^1.0.4" internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" + integrity sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw== + ip@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" @@ -6810,7 +8820,7 @@ ip@^1.1.5: ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-absolute-url@^2.0.0: @@ -6818,6 +8828,21 @@ is-absolute-url@^2.0.0: resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= +is-absolute@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.1.7.tgz#847491119fccb5fb436217cc737f7faad50f603f" + integrity sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA== + dependencies: + is-relative "^0.1.0" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-array-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" @@ -6856,7 +8881,7 @@ is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.2" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -6924,9 +8949,9 @@ is-core-module@^2.2.0: has "^1.0.3" is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== dependencies: has "^1.0.3" @@ -6940,6 +8965,11 @@ is-directory@^0.3.1: resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz" @@ -7003,6 +9033,14 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-interactive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" @@ -7013,6 +9051,11 @@ is-lambda@^1.0.1: resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= +is-mergeable-object@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-mergeable-object/-/is-mergeable-object-1.1.1.tgz#faaa3ed1cfce87d6f7d2f5885e92cc30af3e2ebf" + integrity sha512-CPduJfuGg8h8vW74WOxHtHmtQutyQBzR+3MjQ6iDHIYdbOnm1YC7jv43SqCoU8OPGTJD4nibmiryA4kmogbGrA== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" @@ -7028,6 +9071,11 @@ is-negative-zero@^2.0.2: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + is-number-object@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz" @@ -7055,6 +9103,11 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" @@ -7099,6 +9152,14 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" +is-regex@^1.0.4, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-regex@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz" @@ -7107,13 +9168,10 @@ is-regex@^1.1.3: call-bind "^1.0.2" has-symbols "^1.0.2" -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" +is-relative@^0.1.0: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.1.3.tgz#905fee8ae86f45b3ec614bc3c15c869df0876e82" + integrity sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA== is-resolvable@^1.0.0: version "1.1.0" @@ -7198,6 +9256,11 @@ is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz#f4f54f34d8ebc84a46b93559a036763b6d3e1014" integrity sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ== +is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + is-weakref@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz" @@ -7217,11 +9280,28 @@ is-windows@^1.0.0: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^2.1.1, is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isexe@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + integrity sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -7309,6 +9389,11 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jed@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4" + integrity sha512-z35ZSEcXHxLW4yumw0dF6L464NT36vmx3wxJw8MDpraBcWuNVgUPZgPJKcu1HekNgwlMFNqol7i/IpSbjhqwqA== + jest-changed-files@^27.2.4: version "27.2.4" resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.2.4.tgz" @@ -8214,14 +10299,14 @@ joycon@^3.0.1: resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.6.1: version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -8310,6 +10395,18 @@ jsesc@^2.5.1: resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== + +json-merge-patch@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-merge-patch/-/json-merge-patch-0.2.3.tgz#fa2c6b5af87da77bae2966a589d52e23ed81fe40" + integrity sha512-mjd5eObNGOhWkKCztwVuF25KOzLj2T4TJaWXLBgCQPeoPRJrMxKNgjNBE8sPmXoWRT0WDlo4Itd/gTlFh29TFw== + dependencies: + deep-equal "^1.0.0" + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" @@ -8325,6 +10422,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" @@ -8348,12 +10450,17 @@ json5@2.x, json5@^2.1.2: minimist "^1.2.5" json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + version "1.0.1" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" +json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + 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" @@ -8380,6 +10487,22 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonwebtoken@8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" @@ -8390,6 +10513,40 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jszip@^3.2.2: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + kind-of@^3.0.2: version "3.2.2" resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" @@ -8417,6 +10574,20 @@ kolorist@^1.6.0: resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.6.0.tgz#f43ac794305b30032a5bedcae7799d0f91d2ff36" integrity sha512-dLkz37Ab97HWMx9KTes3Tbi3D1ln9fCAy2zr2YVExJasDRPGRaKcoE4fycWNtnCAJfjFqe0cnY+f8KT2JePEXQ== +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +lcid@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0" + integrity sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg== + dependencies: + invert-kv "^3.0.0" + lerna@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz" @@ -8483,6 +10654,21 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +lighthouse-logger@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz#ba6303e739307c4eee18f08249524e7dafd510db" + integrity sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA== + dependencies: + debug "^2.6.9" + marky "^1.2.2" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" @@ -8541,9 +10727,9 @@ load-yaml-file@^0.2.0: strip-bom "^3.0.0" loader-utils@^1.1.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" - integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + version "1.4.0" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -8591,16 +10777,46 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.memoize@4.1.2, lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" @@ -8611,6 +10827,16 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.startcase@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" @@ -8649,6 +10875,23 @@ log-symbols@^5.1.0: chalk "^5.0.0" is-unicode-supported "^1.1.0" +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -8671,6 +10914,13 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +magic-string@^0.26.2: + version "0.26.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f" + integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow== + dependencies: + sourcemap-codec "^1.4.8" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" @@ -8686,7 +10936,7 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" -make-error@1.x, make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1, make-error@^1.3.2: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -8741,6 +10991,13 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-obj@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" @@ -8790,11 +11047,25 @@ markdownlint@^0.25.1, markdownlint@~0.25.1: dependencies: markdown-it "12.3.2" +marky@^1.2.2: + version "1.2.5" + resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0" + integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== + math-random@^1.0.1: version "1.0.4" resolved "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" @@ -8812,8 +11083,17 @@ mdurl@^1.0.1: media-typer@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3" + integrity sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^2.1.0" + p-is-promise "^2.1.0" meow@^6.0.0: version "6.1.1" @@ -8868,8 +11148,8 @@ meow@^8.0.0: merge-descriptors@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" @@ -8883,8 +11163,8 @@ merge2@^1.3.0, merge2@^1.4.1: methods@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^2.3.11: version "2.3.11" @@ -8931,12 +11211,7 @@ mime-db@1.49.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: +mime-types@^2.1.12, mime-types@~2.1.24: version "2.1.31" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz" integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== @@ -8950,16 +11225,9 @@ mime-types@~2.1.19: dependencies: mime-db "1.49.0" -mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mime@1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.3.1: @@ -8972,12 +11240,17 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -9000,19 +11273,14 @@ minimatch@~3.0.5: minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" - resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: arrify "^1.0.1" is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - -minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -9118,6 +11386,11 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" +mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" @@ -9125,16 +11398,16 @@ mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - modify-values@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +moment@^2.19.3: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + mri@^1.1.0: version "1.1.6" resolved "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz" @@ -9142,20 +11415,25 @@ mri@^1.1.0: ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multimatch@^5.0.0: +multimatch@5.0.0, multimatch@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== @@ -9171,22 +11449,50 @@ 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: +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg== + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + +mz@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nan@^2.14.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + +nanoid@^3.3.1, 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@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.0.tgz#6e144dee117609232c3f415c34b0e550e64999a5" + integrity sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== -negotiator@^0.6.2: +negotiator@0.6.2, negotiator@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== @@ -9222,6 +11528,11 @@ node-fetch@^2.5.0: dependencies: whatwg-url "^5.0.0" +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + node-gyp@^5.0.2: version "5.1.1" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz" @@ -9265,6 +11576,18 @@ node-modules-regexp@^1.0.0: resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= +node-notifier@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.0.tgz#46c5bbecbb796d4a803f646cea5bc91403f2ff38" + integrity sha512-SkwNwGnMMlSPrcoeH4CSo9XyWe72acAHEJGDdPdB+CyBVHsIYaTQ4U/1wk3URsyzC75xZLg2vzU2YaALlqDF1Q== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + node-releases@^1.1.71: version "1.1.73" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz" @@ -9337,6 +11660,11 @@ normalize-url@^3.0.0: resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + normalize-url@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" @@ -9430,7 +11758,7 @@ npm-registry-fetch@^9.0.0: minizlib "^2.0.0" npm-package-arg "^8.0.0" -npm-run-path@^4.0.1: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -9476,7 +11804,7 @@ oauth-sign@~0.9.0: resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -9496,6 +11824,14 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" @@ -9556,10 +11892,15 @@ object.values@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== +on-exit-leak-free@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" + integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" @@ -9577,6 +11918,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" @@ -9626,6 +11975,20 @@ os-homedir@^1.0.0: resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= +os-locale@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0" + integrity sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA== + dependencies: + execa "^4.0.0" + lcid "^3.0.0" + mem "^5.0.0" + +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + integrity sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A== + os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" @@ -9644,6 +12007,16 @@ outdent@^0.5.0: resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== + p-filter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" @@ -9656,6 +12029,11 @@ p-finally@^1.0.0: resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" @@ -9757,6 +12135,16 @@ p-waterfall@^2.1.1: dependencies: p-reduce "^2.0.0" +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + pacote@^11.2.6: version "11.3.5" resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz" @@ -9782,6 +12170,11 @@ pacote@^11.2.6: ssri "^8.0.1" tar "^6.1.0" +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -9799,15 +12192,7 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@5.2.0, parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -9817,6 +12202,14 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parse-path@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" @@ -9858,7 +12251,7 @@ parse5@^7.0.0: parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-browserify@^1.0.1: @@ -9912,8 +12305,8 @@ path-strip-sep@^1.0.10: path-to-regexp@0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^3.0.0: version "3.0.0" @@ -9989,6 +12382,35 @@ pinkie@^2.0.0: resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pino-abstract-transport@v0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" + integrity sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ== + dependencies: + duplexify "^4.1.2" + split2 "^4.0.0" + +pino-std-serializers@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" + integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== + +pino@7.9.1: + version "7.9.1" + resolved "https://registry.yarnpkg.com/pino/-/pino-7.9.1.tgz#7e6f401cac6e80b4eaee908ab90a05e48455cec2" + integrity sha512-28+D7c5orCoScdcWtiPXrCA9tdVosBWrYQgVtPdYTyiuTt6u/+rbEtpJR+dtVG8k1flhv0H2f0XSkgGm+TdjqQ== + dependencies: + fast-redact "^3.0.0" + on-exit-leak-free "^0.2.0" + pino-abstract-transport v0.5.0 + pino-std-serializers "^4.0.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.1.0" + safe-stable-stringify "^2.1.0" + sonic-boom "^2.2.1" + thread-stream "^0.13.0" + pirates@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz" @@ -10032,6 +12454,16 @@ pngjs@^4.0.1: resolved "https://registry.npmjs.org/pngjs/-/pngjs-4.0.1.tgz" integrity sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg== +popmotion@11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.5.tgz#8e3e014421a0ffa30ecd722564fd2558954e1f7d" + integrity sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA== + dependencies: + framesync "6.1.2" + hey-listen "^1.0.8" + style-value-types "5.1.2" + tslib "2.4.0" + postcss-calc@^7.0.1: version "7.0.5" resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz" @@ -10389,6 +12821,15 @@ postcss@6.0.1: source-map "^0.5.6" supports-color "^3.2.3" +postcss@8.4.12: + version "8.4.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" + integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + postcss@^6.0.1, postcss@^6.0.11: version "6.0.23" resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz" @@ -10406,7 +12847,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.16, postcss@^8.4.18: version "8.4.18" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.18.tgz#6d50046ea7d3d66a85e0e782074e7203bc7fbca2" integrity sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA== @@ -10435,6 +12876,11 @@ prelude-ls@~1.1.2: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== + preserve@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" @@ -10477,6 +12923,11 @@ 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== +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + progress@2.0.3, progress@^2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" @@ -10495,6 +12946,13 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +promise-toolbox@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/promise-toolbox/-/promise-toolbox-0.20.0.tgz#af04d7338038c2362b8fb7c27546c57d893bf562" + integrity sha512-VXF6waqUheD19yOU7zxsXhw/HCKlXqXwUc4jM8mchtBqZFNA+GHA7dbJsQDLHP4IUpQuTLpCQRd0lCr5z4CqXQ== + dependencies: + make-error "^1.3.2" + promise.series@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz" @@ -10515,6 +12973,15 @@ promzard@^0.3.0: dependencies: read "1" +prop-types@^15.6.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" @@ -10530,9 +12997,9 @@ protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== -proxy-addr@~2.0.7: +proxy-addr@~2.0.5: version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -10566,6 +13033,13 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + puppeteer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-11.0.0.tgz#0808719c38e15315ecc1b1c28911f1c9054d201f" @@ -10624,7 +13098,12 @@ q@^1.1.2, q@^1.5.1: resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.11.0, qs@^6.9.4: +qs@6.7.0: + version "6.7.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^6.9.4: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -10632,9 +13111,9 @@ qs@6.11.0, qs@^6.9.4: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + version "6.5.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== query-string@^6.13.8: version "6.14.1" @@ -10651,6 +13130,18 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" @@ -10665,32 +13156,139 @@ randomatic@^3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@1.2.8, rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-clientside-effect@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz#29f9b14e944a376b03fb650eed2a754dd128ea3a" + integrity sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg== + dependencies: + "@babel/runtime" "^7.12.13" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-fast-compare@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-focus-lock@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16" + integrity sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg== + dependencies: + "@babel/runtime" "^7.0.0" + focus-lock "^0.11.2" + prop-types "^15.6.2" + react-clientside-effect "^1.2.6" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + +react-icons@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.6.0.tgz#f83eda179af5d02c047449a20b702c858653d397" + integrity sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g== + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + +react-remove-scroll-bar@^2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" + integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A== + dependencies: + react-style-singleton "^2.2.1" + tslib "^2.0.0" + +react-remove-scroll@^2.5.4: + version "2.5.5" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77" + integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== + dependencies: + react-remove-scroll-bar "^2.3.3" + react-style-singleton "^2.2.1" + tslib "^2.1.0" + use-callback-ref "^1.3.0" + use-sidecar "^1.1.2" + +react-router-dom@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.2.tgz#115b37d501d6d8ac870683694978c51c43e6c0d2" + integrity sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ== dependencies: - safe-buffer "^5.1.0" + "@remix-run/router" "1.0.2" + react-router "6.4.2" -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +react-router@6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.2.tgz#300628ee9ed81b8ef1597b5cb98b474efe9779b8" + integrity sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw== + dependencies: + "@remix-run/router" "1.0.2" -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== +react-style-singleton@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" + integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g== dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" + get-nonce "^1.0.0" + invariant "^2.2.4" + tslib "^2.0.0" -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" read-cache@^1.0.0: version "1.0.0" @@ -10803,7 +13401,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.6, readable-stream@~2.3.6: +readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10833,6 +13431,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +real-require@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" + integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== + realpath-native@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz" @@ -10858,6 +13461,11 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.13.4: + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== + regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz" @@ -10865,7 +13473,7 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" -regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -10879,6 +13487,28 @@ regexpp@^3.2.0: resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +registry-auth-token@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" + integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== + dependencies: + rc "1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +relaxed-json@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/relaxed-json/-/relaxed-json-1.0.3.tgz#eb2101ae0ee60e82267d95ed0ddf19a3604b8c1e" + integrity sha512-b7wGPo7o2KE/g7SqkJDDbav6zmrEeP4TK2VpITU72J/M949TLe/23y/ZHJo+pskcGM52xIfFoT9hydwmgr1AEg== + dependencies: + chalk "^2.4.2" + commander "^2.6.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" @@ -10894,7 +13524,7 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request@^2.88.0, request@^2.88.2: +request@2.88.2, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -10925,6 +13555,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-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -10999,6 +13634,13 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== + dependencies: + lowercase-keys "^1.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -11049,6 +13691,13 @@ rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ== + dependencies: + glob "^6.0.1" + rollup-plugin-css-only@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz" @@ -11179,6 +13828,13 @@ rollup@^2.79.1: optionalDependencies: fsevents "~2.3.2" +rollup@~2.78.0: + version "2.78.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.78.1.tgz#52fe3934d9c83cb4f7c4cb5fb75d88591be8648f" + integrity sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg== + optionalDependencies: + fsevents "~2.3.2" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" @@ -11222,21 +13878,26 @@ sade@^1.4.0, sade@^1.7.4: dependencies: mri "^1.1.0" -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-identifier@^0.4.1: version "0.4.2" resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== +safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -11246,12 +13907,17 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-stable-stringify@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.0.tgz#95fadb1bcf8057a1363e11052122f5da36a69215" + integrity sha512-eehKHKpab6E741ud7ZIMcXhKcP6TSIezPkNZhy5U8xC6+VvrRdUA2tMgxGxaGl4cz7c2Ew5+mg5+wNB16KQqrA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@~1.2.4: +sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -11263,6 +13929,20 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + semver-match@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/semver-match/-/semver-match-0.1.1.tgz#e7ccb31f83fd4a0e377d66387afd8ca3a329b5fc" @@ -11282,7 +13962,7 @@ semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7 dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -11301,24 +13981,24 @@ semver@~7.3.0: dependencies: lru-cache "^6.0.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@0.17.1: + version "0.17.1" + resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" + depd "~1.1.2" + destroy "~1.0.4" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "2.0.0" + http-errors "~1.7.2" mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" + ms "2.1.1" + on-finished "~2.3.0" range-parser "~1.2.1" - statuses "2.0.1" + statuses "~1.5.0" serialize-javascript@^4.0.0: version "4.0.0" @@ -11327,25 +14007,38 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.17.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@2.4.11: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" shallow-clone@^3.0.0: version "3.0.1" @@ -11378,6 +14071,16 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -11387,6 +14090,22 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +sign-addon@3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/sign-addon/-/sign-addon-3.11.0.tgz#8c4e688ae8053b8e24ab03aa59e651f1bf66a9fb" + integrity sha512-fcK2WzkMb8e8E9kvuccy+mrBTT81iR+1CowHLU594Elr4E9E9zZFr3itGlL0OoXcRouKmvt7rpXzoARu++tXRQ== + dependencies: + common-tags "1.8.2" + core-js "3.21.0" + deepcopy "2.1.0" + es6-error "4.1.1" + es6-promisify "7.0.0" + jsonwebtoken "8.5.1" + mz "2.7.0" + request "2.88.2" + source-map-support "0.5.21" + stream-to-promise "3.0.0" + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" @@ -11479,6 +14198,13 @@ socks@^2.3.3: ip "^1.1.5" smart-buffer "^4.1.0" +sonic-boom@^2.2.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" + integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== + dependencies: + atomic-sleep "^1.0.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" @@ -11498,7 +14224,15 @@ 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.6, source-map-support@~0.5.20: +source-map-support@0.5.20: + version "0.5.20" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" + integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@0.5.21, source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11506,7 +14240,7 @@ source-map-support@^0.5.6, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -11521,7 +14255,7 @@ source-map@^0.7.3: resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@^1.4.4: +sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -11531,6 +14265,14 @@ spawn-command@^0.0.2-1: resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= +spawn-sync@1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + integrity sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw== + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + spawndamnit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/spawndamnit/-/spawndamnit-2.0.0.tgz#9f762ac5c3476abb994b42ad592b5ad22bb4b0ad" @@ -11577,6 +14319,11 @@ split2@^3.0.0: dependencies: readable-stream "^3.0.0" +split2@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" + integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + split@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" @@ -11584,6 +14331,13 @@ split@^1.0.0: dependencies: through "2" +split@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -11635,10 +14389,31 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-to-array@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== + dependencies: + any-promise "^1.1.0" + +stream-to-promise@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-to-promise/-/stream-to-promise-3.0.0.tgz#8934d66dcbc9189394e8b33200da3bb9611db774" + integrity sha512-h+7wLeFiYegOdgTfTxjRsrT7/Op7grnKEIHWgaO1RTHwcwk7xRreMr3S8XpDfDMesSxzgM2V4CxNCFAGo6ssnA== + dependencies: + any-promise "~1.3.0" + end-of-stream "~1.4.1" + stream-to-array "~2.3.0" stream-transform@^2.1.3: version "2.1.3" @@ -11687,6 +14462,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.0.0, string-width@^4.2.2, 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-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" @@ -11696,15 +14480,6 @@ 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-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11790,16 +14565,31 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-bom-buf@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz#ff9c223937f8e7154b77e9de9bde094186885c15" + integrity sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ== + dependencies: + is-utf8 "^0.2.1" + +strip-bom-stream@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz#4d21a651e723ef743a0a8b0d4534471805330cbb" + integrity sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ== + dependencies: + first-chunk-stream "^3.0.0" + strip-bom-buf "^2.0.0" -strip-bom@^4.0.0: +strip-bom@4.0.0, strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" @@ -11812,11 +14602,16 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" @@ -11838,6 +14633,14 @@ style-inject@^0.3.0: resolved "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz" integrity sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw== +style-value-types@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.1.2.tgz#6be66b237bd546048a764883528072ed95713b62" + integrity sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q== + dependencies: + hey-listen "^1.0.8" + tslib "2.4.0" + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz" @@ -11847,6 +14650,11 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +stylis@4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" + integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" @@ -12051,6 +14859,27 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +thread-stream@^0.13.0: + version "0.13.2" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.13.2.tgz#de8ea87584baee625c631947ec73494aa86131c8" + integrity sha512-woZFt0cLFkPdhsa+IGpRo1jiSouaHxMIljzTgt30CMjBWoUYbbcHqnunW5Yv+BXko9H05MVIcxMipI3Jblallw== + dependencies: + real-require "^0.1.0" + throat@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" @@ -12081,11 +14910,23 @@ timsort@^0.3.0: resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= +tiny-invariant@^1.0.6: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + tinydate@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz" integrity sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w== +tmp@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -12108,6 +14949,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -12115,10 +14961,20 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tosource@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tosource/-/tosource-1.0.0.tgz#42d88dd116618bcf00d6106dd5446f3427902ff1" + integrity sha512-N6g8eQ1eerw6Y1pBhdgkubWIiPFwXa2POSUrlL8jth5CyyEWNWzoGKRkO3CaO7Jx27hlJP54muB3btIAbx4MPg== tough-cookie@^4.0.0: version "4.0.0" @@ -12240,6 +15096,11 @@ ts-node@^7.0.1: source-map-support "^0.5.6" yn "^2.0.0" +tslib@2.4.0, tslib@^2.0.3, tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" @@ -12250,11 +15111,6 @@ tslib@^2.0.0, tslib@^2.2.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - tslib@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" @@ -12390,7 +15246,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -12435,9 +15291,14 @@ type-fest@^1.0.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== -type-is@~1.6.18: +type-fest@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -12546,6 +15407,13 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" @@ -12556,6 +15424,11 @@ universalify@^0.1.0, universalify@^0.1.2: resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" + integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" @@ -12563,8 +15436,8 @@ universalify@^2.0.0: unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unquote@~1.1.1: version "1.1.1" @@ -12580,7 +15453,7 @@ upath2@^3.1.12: path-strip-sep "^1.0.10" tslib "^2.3.1" -upath@^2.0.1: +upath@2.0.1, upath@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== @@ -12593,6 +15466,26 @@ update-browserslist-db@^1.0.9: escalade "^3.1.1" picocolors "^1.0.0" +update-notifier@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" @@ -12600,6 +15493,28 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== + dependencies: + prepend-http "^2.0.0" + +use-callback-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5" + integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w== + dependencies: + tslib "^2.0.0" + +use-sidecar@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2" + integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw== + dependencies: + detect-node-es "^1.1.0" + tslib "^2.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" @@ -12635,14 +15550,19 @@ util.promisify@~1.0.0: utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: version "3.4.0" resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -12684,8 +15604,8 @@ validator@^13.7.0: vary@~1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= vendors@^1.0.0: version "1.0.4" @@ -12714,10 +15634,38 @@ vite-plugin-dts@^1.6.6: kolorist "^1.6.0" ts-morph "^16.0.0" +vite-plugin-web-extension@^1.4.5: + version "1.4.5" + resolved "https://registry.yarnpkg.com/vite-plugin-web-extension/-/vite-plugin-web-extension-1.4.5.tgz#ed52701557c2c54c347a7ae0b85d804bf9c042d8" + integrity sha512-P8+x4XbkFjEfW0fa0HhY6UjU43K6QoEw9PyIXh5yeSsEotj8I1zXw+cWtQoPIsTy03mDswcHaigHOQAQgPGsoA== + dependencies: + ajv "^8.8.1" + md5 "^2.3.0" + web-ext "^6.5.0" + +vite-plugin-zip@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vite-plugin-zip/-/vite-plugin-zip-1.0.1.tgz#a72ad7f29ddc873f7d6f3917397fd008a6badaa3" + integrity sha512-DdQQGq8eSLwMHtFmzwKZ/h59qaeSNsSFM6TKiUsHl2Cx2D1KD0iJD9OQP7rLOSEZCPoUoUvW45G2nJNcr0qlEg== + dependencies: + yazl "^2.5.1" + +vite@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/vite/-/vite-3.1.8.tgz#fa29144167d19b773baffd65b3972ea4c12359c9" + integrity sha512-m7jJe3nufUbuOfotkntGFupinL/fmuTNuQmiVE7cH2IZMuf4UbfbGYMUT3jVWgGYuRVLY9j8NnrRqgw5rr5QTg== + dependencies: + esbuild "^0.15.9" + postcss "^8.4.16" + resolve "^1.22.1" + rollup "~2.78.0" + optionalDependencies: + fsevents "~2.3.2" + vite@^3.2.0-beta.2: - version "3.2.0-beta.3" - resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.0-beta.3.tgz#a42499e0f2fbe761c60e5438f0a70bd321e5a77a" - integrity sha512-c9KRnMUlbxLNqzCihTozhKlRdOzjAWsO3H9cC/ej+JuvL4rnIxSvv+9m5Wz2PAs9lgDD7Yk0VZuzKYSSk3peWw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-3.2.2.tgz#280762bfaf47bcea1d12698427331c0009ac7c1f" + integrity sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw== dependencies: esbuild "^0.15.9" postcss "^8.4.18" @@ -12747,6 +15695,14 @@ walker@^1.0.7: dependencies: makeerror "1.0.x" +watchpack@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" + integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + 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 +15710,47 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-ext@^6.5.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/web-ext/-/web-ext-6.8.0.tgz#bc8d1ee478a0a1444f48159e80d492f7bf2fb9fc" + integrity sha512-qZ3a4YVs0Vdqet44QRZEcNUQznkrfhsAkSOnZp57O4T4A9Bo3pamePSBeRqdPdJv9GF8ntKG84o3eV0MrEvLbw== + dependencies: + "@babel/runtime" "7.13.9" + "@devicefarmer/adbkit" "2.11.3" + addons-linter "4.14.0" + bunyan "1.8.15" + camelcase "6.2.0" + chrome-launcher "0.15.0" + debounce "1.2.0" + decamelize "5.0.0" + es6-error "4.1.1" + firefox-profile "4.2.2" + fs-extra "9.1.0" + fx-runner "1.2.0" + import-fresh "3.3.0" + mkdirp "1.0.4" + multimatch "5.0.0" + mz "2.7.0" + node-notifier "9.0.0" + open "7.4.2" + parse-json "5.2.0" + promise-toolbox "0.20.0" + sign-addon "3.11.0" + source-map-support "0.5.20" + strip-bom "4.0.0" + strip-json-comments "3.1.1" + tmp "0.2.1" + update-notifier "5.1.0" + watchpack "2.1.1" + ws "7.4.6" + yargs "16.2.0" + zip-dir "2.0.0" + +webextension-polyfill@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz#ccb28101c910ba8cf955f7e6a263e662d744dbb8" + integrity sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" @@ -12798,6 +15795,11 @@ whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" +when@3.7.7: + version "3.7.7" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" + integrity sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw== + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" @@ -12834,6 +15836,14 @@ which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" +which@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.4.tgz#1557f96080604e5b11b3599eb9f45b50a9efd722" + integrity sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA== + dependencies: + is-absolute "^0.1.7" + isexe "^1.1.1" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" @@ -12855,6 +15865,18 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +winreg@0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.12.tgz#07105554ba1a9d08979251d129475bffae3006b7" + integrity sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ== + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" @@ -12949,6 +15971,11 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + ws@8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" @@ -12974,11 +16001,29 @@ ws@^7.4.6: resolved "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + 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== +xml2js@~0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -13037,11 +16082,37 @@ 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.0, 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== +yargs@16.2.0, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@17.4.0: + version "17.4.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00" + integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + yargs@^15.1.0: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -13059,19 +16130,6 @@ yargs@^15.1.0: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^17.1.1: version "17.6.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" @@ -13085,7 +16143,7 @@ yargs@^17.1.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yauzl@^2.10.0: +yauzl@2.10.0, yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= @@ -13093,6 +16151,13 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +yazl@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" + integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== + dependencies: + buffer-crc32 "~0.2.3" + yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" @@ -13118,3 +16183,11 @@ z-schema@~5.0.2: validator "^13.7.0" optionalDependencies: commander "^2.20.3" + +zip-dir@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/zip-dir/-/zip-dir-2.0.0.tgz#c5df6e15c8f9efeb4e320377028c9f5c8277c932" + integrity sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg== + dependencies: + async "^3.2.0" + jszip "^3.2.2" From 3cc4323094065a12f8b65afecd45061d604e245f Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Tue, 14 Feb 2023 21:54:30 +1100 Subject: [PATCH 130/201] improve rrdom performance (#1127) * add more check to rrdom to make diff algorithm more robust * fix: selector match in iframe is case-insensitive add try catch to some fragile points * test: increase timeout value for Jest * improve code style * fix: failed to execute insertBefore on Node in the diff function this happens when ids of doctype or html element are changed in the virtual dom also improve the code quality * refactor diff function to make the code cleaner * fix: virtual nodes are passed to plugin's onBuild function * refactor the diff function and adjust the order of diff work. * call afterAppend hook in a consistent traversal order * improve the performance of the "contains" function reduce the complexity from O(n) to O(logn) a specific benchmark is needed to add further * add a real events for benchmark * refactor: change the data structure of childNodes from array to linked list * remove legacy code in rrweb package * update unit tests * update change log --- .changeset/serious-ants-juggle.md | 10 + packages/rrdom/src/document.ts | 188 ++++++++------ packages/rrdom/src/index.ts | 3 +- packages/rrdom/test/diff.test.ts | 2 +- packages/rrdom/test/document.test.ts | 237 ++++++++++++++---- packages/rrweb/src/rrdom/index.ts | 173 ------------- packages/rrweb/src/rrdom/tree-node.ts | 51 ---- .../benchmark/replay-fast-forward.test.ts | 53 +++- 8 files changed, 364 insertions(+), 353 deletions(-) create mode 100644 .changeset/serious-ants-juggle.md delete mode 100644 packages/rrweb/src/rrdom/index.ts delete mode 100644 packages/rrweb/src/rrdom/tree-node.ts diff --git a/.changeset/serious-ants-juggle.md b/.changeset/serious-ants-juggle.md new file mode 100644 index 0000000000..e475e12267 --- /dev/null +++ b/.changeset/serious-ants-juggle.md @@ -0,0 +1,10 @@ +--- +'rrdom': major +'rrdom-nodejs': major +'rrweb': patch +--- + +Refactor: Improve performance by 80% in a super large benchmark case. + +1. Refactor: change the data structure of childNodes from array to linked list +2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index 6af6e8198d..98ee1696e4 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -3,8 +3,8 @@ import { parseCSSText, camelize, toCSSText } from './style'; export interface IRRNode { parentElement: IRRNode | null; parentNode: IRRNode | null; - childNodes: IRRNode[]; ownerDocument: IRRDocument; + readonly childNodes: IRRNode[]; readonly ELEMENT_NODE: number; readonly TEXT_NODE: number; // corresponding nodeType value of standard HTML Node @@ -16,8 +16,11 @@ export interface IRRNode { lastChild: IRRNode | null; + previousSibling: IRRNode | null; + nextSibling: IRRNode | null; + // If the node is a document or a doctype, textContent returns null. textContent: string | null; contains(node: IRRNode): boolean; @@ -127,11 +130,16 @@ type ConstrainedConstructor> = new ( * This is designed as an abstract class so it should never be instantiated. */ export class BaseRRNode implements IRRNode { - public childNodes: IRRNode[] = []; public parentElement: IRRNode | null = null; public parentNode: IRRNode | null = null; - public textContent: string | null; public ownerDocument: IRRDocument; + public firstChild: IRRNode | null = null; + public lastChild: IRRNode | null = null; + public previousSibling: IRRNode | null = null; + public nextSibling: IRRNode | null = null; + + public textContent: string | null; + public readonly ELEMENT_NODE: number = NodeType.ELEMENT_NODE; public readonly TEXT_NODE: number = NodeType.TEXT_NODE; // corresponding nodeType value of standard HTML Node @@ -144,26 +152,24 @@ export class BaseRRNode implements IRRNode { // } - public get firstChild(): IRRNode | null { - return this.childNodes[0] || null; - } - - public get lastChild(): IRRNode | null { - return this.childNodes[this.childNodes.length - 1] || null; - } - - public get nextSibling(): IRRNode | null { - const parentNode = this.parentNode; - if (!parentNode) return null; - const siblings = parentNode.childNodes; - const index = siblings.indexOf(this); - return siblings[index + 1] || null; + public get childNodes(): IRRNode[] { + const childNodes: IRRNode[] = []; + let childIterator: IRRNode | null = this.firstChild; + while (childIterator) { + childNodes.push(childIterator); + childIterator = childIterator.nextSibling; + } + return childNodes; } public contains(node: IRRNode) { - if (node === this) return true; - for (const child of this.childNodes) { - if (child.contains(node)) return true; + if (!(node instanceof BaseRRNode)) return false; + else if (node.ownerDocument !== this.ownerDocument) return false; + else if (node === this) return true; + + while (node.parentNode) { + if (node.parentNode === this) return true; + node = node.parentNode; } return false; } @@ -202,11 +208,11 @@ export function BaseRRDocumentImpl< public readonly nodeName: '#document' = '#document'; public readonly compatMode: 'BackCompat' | 'CSS1Compat' = 'CSS1Compat'; public readonly RRNodeType = RRNodeType.Document; - public textContent: string | null = null; // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(...args: any[]) { super(args); + this.textContent = null; this.ownerDocument = this; } @@ -248,8 +254,8 @@ export function BaseRRDocumentImpl< return this.documentElement; } - public appendChild(childNode: IRRNode): IRRNode { - const nodeType = childNode.RRNodeType; + public appendChild(newChild: IRRNode): IRRNode { + const nodeType = newChild.RRNodeType; if ( nodeType === RRNodeType.Element || nodeType === RRNodeType.DocumentType @@ -262,11 +268,10 @@ export function BaseRRDocumentImpl< ); } } - childNode.parentElement = null; - childNode.parentNode = this; - childNode.ownerDocument = this.ownerDocument; - this.childNodes.push(childNode); - return childNode; + + const child = appendChild(this, newChild); + child.parentElement = null; + return child; } public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode { @@ -283,33 +288,19 @@ export function BaseRRDocumentImpl< ); } } - if (refChild === null) return this.appendChild(newChild); - const childIndex = this.childNodes.indexOf(refChild); - if (childIndex == -1) - throw new Error( - "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", - ); - this.childNodes.splice(childIndex, 0, newChild); - newChild.parentElement = null; - newChild.parentNode = this; - newChild.ownerDocument = this.ownerDocument; - return newChild; - } - - public removeChild(node: IRRNode) { - const indexOfChild = this.childNodes.indexOf(node); - if (indexOfChild === -1) - throw new Error( - "Failed to execute 'removeChild' on 'RRDocument': The RRNode to be removed is not a child of this RRNode.", - ); - this.childNodes.splice(indexOfChild, 1); - node.parentElement = null; - node.parentNode = null; - return node; + + const child = insertBefore(this, newChild, refChild); + child.parentElement = null; + return child; + } + + public removeChild(node: IRRNode): IRRNode { + return removeChild(this, node); } public open() { - this.childNodes = []; + this.firstChild = null; + this.lastChild = null; } public close() { @@ -415,7 +406,6 @@ export function BaseRRDocumentTypeImpl< public readonly name: string; public readonly publicId: string; public readonly systemId: string; - public textContent: string | null = null; constructor(qualifiedName: string, publicId: string, systemId: string) { super(); @@ -423,6 +413,7 @@ export function BaseRRDocumentTypeImpl< this.publicId = publicId; this.systemId = systemId; this.nodeName = qualifiedName; + this.textContent = null; } toString() { @@ -459,7 +450,9 @@ export function BaseRRElementImpl< } public set textContent(textContent: string) { - this.childNodes = [this.ownerDocument.createTextNode(textContent)]; + this.firstChild = null; + this.lastChild = null; + this.appendChild(this.ownerDocument.createTextNode(textContent)); } public get classList(): ClassList { @@ -528,37 +521,15 @@ export function BaseRRElementImpl< } public appendChild(newChild: IRRNode): IRRNode { - this.childNodes.push(newChild); - newChild.parentNode = this; - newChild.parentElement = this; - newChild.ownerDocument = this.ownerDocument; - return newChild; + return appendChild(this, newChild); } public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode { - if (refChild === null) return this.appendChild(newChild); - const childIndex = this.childNodes.indexOf(refChild); - if (childIndex == -1) - throw new Error( - "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", - ); - this.childNodes.splice(childIndex, 0, newChild); - newChild.parentElement = this; - newChild.parentNode = this; - newChild.ownerDocument = this.ownerDocument; - return newChild; + return insertBefore(this, newChild, refChild); } public removeChild(node: IRRNode): IRRNode { - const indexOfChild = this.childNodes.indexOf(node); - if (indexOfChild === -1) - throw new Error( - "Failed to execute 'removeChild' on 'RRElement': The RRNode to be removed is not a child of this RRNode.", - ); - this.childNodes.splice(indexOfChild, 1); - node.parentElement = null; - node.parentNode = null; - return node; + return removeChild(this, node); } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -741,6 +712,65 @@ export type CSSStyleDeclaration = Record & { removeProperty: (name: string) => string; }; +function appendChild(parent: IRRNode, newChild: IRRNode) { + if (parent.lastChild) { + parent.lastChild.nextSibling = newChild; + newChild.previousSibling = parent.lastChild; + } else { + parent.firstChild = newChild; + newChild.previousSibling = null; + } + parent.lastChild = newChild; + newChild.nextSibling = null; + newChild.parentNode = parent; + newChild.parentElement = parent; + newChild.ownerDocument = parent.ownerDocument; + return newChild; +} + +function insertBefore( + parent: IRRNode, + newChild: IRRNode, + refChild: IRRNode | null, +) { + if (!refChild) return appendChild(parent, newChild); + + if (refChild.parentNode !== parent) + throw new Error( + "Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.", + ); + + newChild.previousSibling = refChild.previousSibling; + refChild.previousSibling = newChild; + newChild.nextSibling = refChild; + + if (newChild.previousSibling) newChild.previousSibling.nextSibling = newChild; + else parent.firstChild = newChild; + + newChild.parentElement = parent; + newChild.parentNode = parent; + newChild.ownerDocument = parent.ownerDocument; + return newChild; +} + +function removeChild(parent: IRRNode, child: IRRNode) { + if (child.parentNode !== parent) + throw new Error( + "Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode.", + ); + if (child.previousSibling) + child.previousSibling.nextSibling = child.nextSibling; + else parent.firstChild = child.nextSibling; + if (child.nextSibling) + child.nextSibling.previousSibling = child.previousSibling; + else parent.lastChild = child.previousSibling; + child.previousSibling = null; + child.nextSibling = null; + child.parentElement = null; + child.parentNode = null; + return child; +} + // Enumerate nodeType value of standard HTML Node. export enum NodeType { PLACEHOLDER, // This isn't a node type. Enum type value starts from zero but NodeType value starts from 1. diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index eda0dd1912..eeddb03e1c 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -131,7 +131,8 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) { } destroyTree() { - this.childNodes = []; + this.firstChild = null; + this.lastChild = null; this.mirror.reset(); } diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 77595adea6..04d638c314 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -1271,7 +1271,7 @@ describe('diff algorithm for rrdom', () => { expect(rrdom.mirror.getId(rrdom)).toBe(-2); expect(rrdom.mirror.getId(rrdom.body)).toBe(-6); - rrdom.childNodes = []; + while (rrdom.firstChild) rrdom.removeChild(rrdom.firstChild); /** * Rebuild the rrdom and make it looks like this: * -7 RRDocument diff --git a/packages/rrdom/test/document.test.ts b/packages/rrdom/test/document.test.ts index 505cc700e4..421dc036e2 100644 --- a/packages/rrdom/test/document.test.ts +++ b/packages/rrdom/test/document.test.ts @@ -9,6 +9,7 @@ import { BaseRRMediaElementImpl, BaseRRNode, IRRDocumentType, + IRRNode, } from '../src/document'; describe('Basic RRDocument implementation', () => { @@ -34,6 +35,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -42,56 +44,103 @@ describe('Basic RRDocument implementation', () => { expect(node.toString()).toEqual('RRNode'); }); - it('can get first child node', () => { + it('can get and set first child node', () => { const parentNode = new RRNode(); const childNode1 = new RRNode(); - const childNode2 = new RRNode(); expect(parentNode.firstChild).toBeNull(); - parentNode.childNodes = [childNode1]; - expect(parentNode.firstChild).toBe(childNode1); - parentNode.childNodes = [childNode1, childNode2]; + parentNode.firstChild = childNode1; expect(parentNode.firstChild).toBe(childNode1); - parentNode.childNodes = [childNode2, childNode1]; - expect(parentNode.firstChild).toBe(childNode2); + parentNode.firstChild = null; + expect(parentNode.firstChild).toBeNull(); }); - it('can get last child node', () => { + it('can get and set last child node', () => { const parentNode = new RRNode(); const childNode1 = new RRNode(); - const childNode2 = new RRNode(); expect(parentNode.lastChild).toBeNull(); - parentNode.childNodes = [childNode1]; - expect(parentNode.lastChild).toBe(childNode1); - parentNode.childNodes = [childNode1, childNode2]; - expect(parentNode.lastChild).toBe(childNode2); - parentNode.childNodes = [childNode2, childNode1]; + parentNode.lastChild = childNode1; expect(parentNode.lastChild).toBe(childNode1); + parentNode.lastChild = null; + expect(parentNode.lastChild).toBeNull(); + }); + + it('can get and set preSibling', () => { + const node1 = new RRNode(); + const node2 = new RRNode(); + expect(node1.previousSibling).toBeNull(); + node1.previousSibling = node2; + expect(node1.previousSibling).toBe(node2); + node1.previousSibling = null; + expect(node1.previousSibling).toBeNull(); + }); + + it('can get and set nextSibling', () => { + const node1 = new RRNode(); + const node2 = new RRNode(); + expect(node1.nextSibling).toBeNull(); + node1.nextSibling = node2; + expect(node1.nextSibling).toBe(node2); + node1.nextSibling = null; + expect(node1.nextSibling).toBeNull(); }); - it('can get nextSibling', () => { + it('can get childNodes', () => { const parentNode = new RRNode(); + expect(parentNode.childNodes).toBeInstanceOf(Array); + expect(parentNode.childNodes.length).toBe(0); + const childNode1 = new RRNode(); + parentNode.firstChild = childNode1; + parentNode.lastChild = childNode1; + expect(parentNode.childNodes).toEqual([childNode1]); + const childNode2 = new RRNode(); - expect(parentNode.nextSibling).toBeNull(); - expect(childNode1.nextSibling).toBeNull(); - childNode1.parentNode = parentNode; - parentNode.childNodes = [childNode1]; - expect(childNode1.nextSibling).toBeNull(); - childNode2.parentNode = parentNode; - parentNode.childNodes = [childNode1, childNode2]; - expect(childNode1.nextSibling).toBe(childNode2); - expect(childNode2.nextSibling).toBeNull(); + parentNode.lastChild = childNode2; + childNode1.nextSibling = childNode2; + childNode2.previousSibling = childNode1; + expect(parentNode.childNodes).toEqual([childNode1, childNode2]); + + const childNode3 = new RRNode(); + parentNode.lastChild = childNode3; + childNode2.nextSibling = childNode3; + childNode3.previousSibling = childNode2; + expect(parentNode.childNodes).toEqual([ + childNode1, + childNode2, + childNode3, + ]); }); it('should return whether the node contains another node', () => { const parentNode = new RRNode(); + expect(parentNode.contains(parentNode)).toBeTruthy(); + expect(parentNode.contains(null as unknown as IRRNode)).toBeFalsy(); + expect(parentNode.contains(undefined as unknown as IRRNode)).toBeFalsy(); + expect(parentNode.contains({} as unknown as IRRNode)).toBeFalsy(); + expect( + parentNode.contains(new RRDocument().createElement('div')), + ).toBeFalsy(); const childNode1 = new RRNode(); const childNode2 = new RRNode(); - parentNode.childNodes = [childNode1]; + parentNode.firstChild = childNode1; + parentNode.lastChild = childNode1; + childNode1.parentNode = parentNode; expect(parentNode.contains(childNode1)).toBeTruthy(); expect(parentNode.contains(childNode2)).toBeFalsy(); - childNode1.childNodes = [childNode2]; + + parentNode.lastChild = childNode2; + childNode1.nextSibling = childNode2; + childNode2.previousSibling = childNode1; + childNode2.parentNode = childNode1; + expect(parentNode.contains(childNode1)).toBeTruthy(); expect(parentNode.contains(childNode2)).toBeTruthy(); + + const childNode3 = new RRNode(); + expect(parentNode.contains(childNode3)).toBeFalsy(); + childNode2.firstChild = childNode3; + childNode2.lastChild = childNode3; + childNode3.parentNode = childNode2; + expect(parentNode.contains(childNode3)).toBeTruthy(); }); it('should not implement appendChild', () => { @@ -143,6 +192,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -282,7 +332,7 @@ describe('Basic RRDocument implementation', () => { expect(() => node.removeChild(node.createElement('div')), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to execute 'removeChild' on 'RRDocument': The RRNode to be removed is not a child of this RRNode."`, + `"Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode."`, ); expect(node.removeChild(documentType)).toBe(documentType); expect(documentType.parentNode).toBeNull(); @@ -369,6 +419,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -404,6 +455,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -686,23 +738,39 @@ describe('Basic RRDocument implementation', () => { const node = document.createElement('div'); expect(node.childNodes.length).toBe(0); - const child1 = document.createComment('span'); + const child1 = document.createElement('span'); expect(node.appendChild(child1)).toBe(child1); - expect(node.childNodes[0]).toEqual(child1); + expect(node.childNodes[0]).toBe(child1); + expect(node.firstChild).toBe(child1); + expect(node.lastChild).toBe(child1); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBeNull(); expect(child1.parentElement).toBe(node); expect(child1.parentNode).toBe(node); + expect(child1.ownerDocument).toBe(document); + expect(node.contains(child1)).toBeTruthy(); const child2 = document.createElement('p'); expect(node.appendChild(child2)).toBe(child2); - expect(node.childNodes[1]).toEqual(child2); + expect(node.childNodes[1]).toBe(child2); + expect(node.firstChild).toBe(child1); + expect(node.lastChild).toBe(child2); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBe(child2); + expect(child2.previousSibling).toBe(child1); + expect(child2.nextSibling).toBeNull(); expect(child2.parentElement).toBe(node); expect(child2.parentNode).toBe(node); + expect(child2.ownerDocument).toBe(document); + expect(node.contains(child1)).toBeTruthy(); + expect(node.contains(child2)).toBeTruthy(); }); it('can insert new child before an existing child', () => { const node = document.createElement('div'); const child1 = document.createElement('h1'); const child2 = document.createElement('h2'); + const child3 = document.createElement('h3'); expect(() => node.insertBefore(node, child1), ).toThrowErrorMatchingInlineSnapshot( @@ -710,42 +778,119 @@ describe('Basic RRDocument implementation', () => { ); expect(node.insertBefore(child1, null)).toBe(child1); expect(node.childNodes[0]).toBe(child1); + expect(node.childNodes.length).toBe(1); + expect(node.firstChild).toBe(child1); + expect(node.lastChild).toBe(child1); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBeNull(); expect(child1.parentNode).toBe(node); expect(child1.parentElement).toBe(node); + expect(child1.ownerDocument).toBe(document); + expect(node.contains(child1)).toBeTruthy(); expect(node.insertBefore(child2, child1)).toBe(child2); - expect(node.childNodes.length).toBe(2); - expect(node.childNodes[0]).toBe(child2); - expect(node.childNodes[1]).toBe(child1); + expect(node.childNodes).toEqual([child2, child1]); + expect(node.firstChild).toBe(child2); + expect(node.lastChild).toBe(child1); + expect(child1.previousSibling).toBe(child2); + expect(child1.nextSibling).toBeNull(); + expect(child2.previousSibling).toBeNull(); + expect(child2.nextSibling).toBe(child1); expect(child2.parentNode).toBe(node); expect(child2.parentElement).toBe(node); + expect(child2.ownerDocument).toBe(document); + expect(node.contains(child2)).toBeTruthy(); + expect(node.contains(child1)).toBeTruthy(); + + expect(node.insertBefore(child3, child1)).toBe(child3); + expect(node.childNodes).toEqual([child2, child3, child1]); + expect(node.firstChild).toBe(child2); + expect(node.lastChild).toBe(child1); + expect(child1.previousSibling).toBe(child3); + expect(child1.nextSibling).toBeNull(); + expect(child3.previousSibling).toBe(child2); + expect(child3.nextSibling).toBe(child1); + expect(child2.previousSibling).toBeNull(); + expect(child2.nextSibling).toBe(child3); + expect(child3.parentNode).toBe(node); + expect(child3.parentElement).toBe(node); + expect(child3.ownerDocument).toBe(document); + expect(node.contains(child2)).toBeTruthy(); + expect(node.contains(child3)).toBeTruthy(); + expect(node.contains(child1)).toBeTruthy(); }); it('can remove an existing child', () => { const node = document.createElement('div'); const child1 = document.createElement('h1'); const child2 = document.createElement('h2'); + const child3 = document.createElement('h3'); node.appendChild(child1); node.appendChild(child2); - expect(node.childNodes.length).toBe(2); - expect(child1.parentNode).toBe(node); - expect(child2.parentNode).toBe(node); - expect(child1.parentElement).toBe(node); - expect(child2.parentElement).toBe(node); + node.appendChild(child3); + expect(node.childNodes).toEqual([child1, child2, child3]); expect(() => node.removeChild(document.createElement('div')), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to execute 'removeChild' on 'RRElement': The RRNode to be removed is not a child of this RRNode."`, + `"Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode."`, ); - expect(node.removeChild(child1)).toBe(child1); - expect(child1.parentNode).toBeNull(); - expect(child1.parentElement).toBeNull(); - expect(node.childNodes.length).toBe(1); + // Remove the middle child. expect(node.removeChild(child2)).toBe(child2); - expect(node.childNodes.length).toBe(0); + expect(node.childNodes).toEqual([child1, child3]); + expect(node.contains(child2)).toBeFalsy(); + expect(node.firstChild).toBe(child1); + expect(node.lastChild).toBe(child3); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBe(child3); + expect(child3.previousSibling).toBe(child1); + expect(child3.nextSibling).toBeNull(); + expect(child2.previousSibling).toBeNull(); + expect(child2.nextSibling).toBeNull(); expect(child2.parentNode).toBeNull(); expect(child2.parentElement).toBeNull(); + + // Remove the previous child. + expect(node.removeChild(child1)).toBe(child1); + expect(node.childNodes).toEqual([child3]); + expect(node.contains(child1)).toBeFalsy(); + expect(node.firstChild).toBe(child3); + expect(node.lastChild).toBe(child3); + expect(child3.previousSibling).toBeNull(); + expect(child3.nextSibling).toBeNull(); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBeNull(); + expect(child1.parentNode).toBeNull(); + expect(child1.parentElement).toBeNull(); + + node.insertBefore(child1, child3); + expect(node.childNodes).toEqual([child1, child3]); + // Remove the next child. + expect(node.removeChild(child3)).toBe(child3); + expect(node.childNodes).toEqual([child1]); + expect(node.contains(child3)).toBeFalsy(); + expect(node.contains(child1)).toBeTruthy(); + expect(node.firstChild).toBe(child1); + expect(node.lastChild).toBe(child1); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBeNull(); + expect(child3.previousSibling).toBeNull(); + expect(child3.nextSibling).toBeNull(); + expect(child3.parentNode).toBeNull(); + expect(child3.parentElement).toBeNull(); + + // Remove all children. + expect(node.removeChild(child1)).toBe(child1); + expect(node.childNodes).toEqual([]); + expect(node.contains(child1)).toBeFalsy(); + expect(node.contains(child2)).toBeFalsy(); + expect(node.contains(child3)).toBeFalsy(); + expect(node.firstChild).toBeNull(); + expect(node.lastChild).toBeNull(); + expect(child1.previousSibling).toBeNull(); + expect(child1.nextSibling).toBeNull(); + expect(child1.parentNode).toBeNull(); + expect(child1.parentElement).toBeNull(); }); }); @@ -768,6 +913,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -803,6 +949,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -838,6 +985,7 @@ describe('Basic RRDocument implementation', () => { expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); expect(node.lastChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); @@ -870,6 +1018,7 @@ describe('Basic RRDocument implementation', () => { expect(node.ELEMENT_NODE).toBe(document.ELEMENT_NODE); expect(node.TEXT_NODE).toBe(document.TEXT_NODE); expect(node.firstChild).toBeNull(); + expect(node.previousSibling).toBeNull(); expect(node.nextSibling).toBeNull(); expect(node.contains).toBeDefined(); expect(node.appendChild).toBeDefined(); diff --git a/packages/rrweb/src/rrdom/index.ts b/packages/rrweb/src/rrdom/index.ts deleted file mode 100644 index 28b5e59040..0000000000 --- a/packages/rrweb/src/rrdom/index.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { RRdomTreeNode, AnyObject } from './tree-node'; - -class RRdomTree { - private readonly symbol = '__rrdom__'; - - public initialize(object: AnyObject) { - this._node(object); - - return object; - } - - public hasChildren(object: AnyObject): boolean { - return Boolean(this._node(object).hasChildren); - } - - public firstChild(object: AnyObject) { - return this._node(object).firstChild || null; - } - - public lastChild(object: AnyObject) { - return this._node(object).lastChild || null; - } - - public previousSibling(object: AnyObject) { - return this._node(object).previousSibling || null; - } - - public nextSibling(object: AnyObject) { - return this._node(object).nextSibling || null; - } - - public parent(object: AnyObject) { - return this._node(object).parent || null; - } - - public insertAfter(referenceObject: AnyObject, newObject: AnyObject) { - const referenceNode = this._node(referenceObject); - const nextNode = this._node(referenceNode.nextSibling); - const newNode = this._node(newObject); - const parentNode = this._node(referenceNode.parent); - - if (newNode.isAttached) { - throw new Error('Node already attached'); - } - if (!referenceNode) { - throw new Error('Reference node not attached'); - } - - newNode.parent = referenceNode.parent; - newNode.previousSibling = referenceObject; - newNode.nextSibling = referenceNode.nextSibling; - referenceNode.nextSibling = newObject; - - if (nextNode) { - nextNode.previousSibling = newObject; - } - - if (parentNode && parentNode.lastChild === referenceObject) { - parentNode.lastChild = newObject; - } - - if (parentNode) { - parentNode.childrenChanged(); - } - - return newObject; - } - - public insertBefore(referenceObject: AnyObject, newObject: AnyObject) { - const referenceNode = this._node(referenceObject); - const prevNode = this._node(referenceNode.previousSibling); - const newNode = this._node(newObject); - const parentNode = this._node(referenceNode.parent); - - if (newNode.isAttached) { - throw new Error('Node already attached'); - } - if (!referenceNode) { - throw new Error('Reference node not attached'); - } - - newNode.parent = referenceNode.parent; - newNode.previousSibling = referenceNode.previousSibling; - newNode.nextSibling = referenceObject; - referenceNode.previousSibling = newObject; - - if (prevNode) { - prevNode.nextSibling = newObject; - } - - if (parentNode && parentNode.firstChild === referenceObject) { - parentNode.firstChild = newObject; - } - - if (parentNode) { - parentNode.childrenChanged(); - } - - return newObject; - } - - public appendChild(referenceObject: AnyObject, newObject: AnyObject) { - const referenceNode = this._node(referenceObject); - const newNode = this._node(newObject); - - if (newNode.isAttached) { - throw new Error('Node already attached'); - } - if (!referenceNode) { - throw new Error('Reference node not attached'); - } - - if (referenceNode.hasChildren) { - this.insertAfter(referenceNode.lastChild!, newObject); - } else { - newNode.parent = referenceObject; - referenceNode.firstChild = newObject; - referenceNode.lastChild = newObject; - referenceNode.childrenChanged(); - } - - return newObject; - } - - public remove(removeObject: AnyObject) { - const removeNode = this._node(removeObject); - const parentNode = this._node(removeNode.parent); - const prevNode = this._node(removeNode.previousSibling); - const nextNode = this._node(removeNode.nextSibling); - - if (parentNode) { - if (parentNode.firstChild === removeObject) { - parentNode.firstChild = removeNode.nextSibling; - } - - if (parentNode.lastChild === removeObject) { - parentNode.lastChild = removeNode.previousSibling; - } - } - - if (prevNode) { - prevNode.nextSibling = removeNode.nextSibling; - } - - if (nextNode) { - nextNode.previousSibling = removeNode.previousSibling; - } - - removeNode.parent = null; - removeNode.previousSibling = null; - removeNode.nextSibling = null; - removeNode.cachedIndex = -1; - removeNode.cachedIndexVersion = NaN; - - if (parentNode) { - parentNode.childrenChanged(); - } - - return removeObject; - } - - private _node(object: AnyObject | null): RRdomTreeNode { - if (!object) { - throw new Error('Object is falsy'); - } - - if (this.symbol in object) { - return object[this.symbol] as RRdomTreeNode; - } - - return (object[this.symbol] = new RRdomTreeNode()); - } -} diff --git a/packages/rrweb/src/rrdom/tree-node.ts b/packages/rrweb/src/rrdom/tree-node.ts deleted file mode 100644 index 88839ee670..0000000000 --- a/packages/rrweb/src/rrdom/tree-node.ts +++ /dev/null @@ -1,51 +0,0 @@ -export type AnyObject = { [key: string]: any; __rrdom__?: RRdomTreeNode }; - -export class RRdomTreeNode implements AnyObject { - public parent: AnyObject | null = null; - public previousSibling: AnyObject | null = null; - public nextSibling: AnyObject | null = null; - - public firstChild: AnyObject | null = null; - public lastChild: AnyObject | null = null; - - // This value is incremented anytime a children is added or removed - public childrenVersion = 0; - // The last child object which has a cached index - public childIndexCachedUpTo: AnyObject | null = null; - - /** - * This value represents the cached node index, as long as - * cachedIndexVersion matches with the childrenVersion of the parent - */ - public cachedIndex = -1; - public cachedIndexVersion = NaN; - - public get isAttached() { - return Boolean(this.parent || this.previousSibling || this.nextSibling); - } - - public get hasChildren() { - return Boolean(this.firstChild); - } - - public childrenChanged() { - this.childrenVersion = (this.childrenVersion + 1) & 0xffffffff; - this.childIndexCachedUpTo = null; - } - - public getCachedIndex(parentNode: AnyObject) { - if (this.cachedIndexVersion !== parentNode.childrenVersion) { - this.cachedIndexVersion = NaN; - // cachedIndex is no longer valid - return -1; - } - - return this.cachedIndex; - } - - public setCachedIndex(parentNode: AnyObject, index: number) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - this.cachedIndexVersion = parentNode.childrenVersion; - this.cachedIndex = index; - } -} diff --git a/packages/rrweb/test/benchmark/replay-fast-forward.test.ts b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts index b5bb71a378..6fbed1a055 100644 --- a/packages/rrweb/test/benchmark/replay-fast-forward.test.ts +++ b/packages/rrweb/test/benchmark/replay-fast-forward.test.ts @@ -1,12 +1,14 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as https from 'https'; import type { eventWithTime } from '@rrweb/types'; import type { recordOptions } from '../../src/types'; import { launchPuppeteer, ISuite } from '../utils'; const suites: Array<{ title: string; - eval: string; + eval?: string; + eventURL?: string; eventsString?: string; times?: number; // defaults to 5 }> = [ @@ -66,6 +68,12 @@ const suites: Array<{ `, times: 3, }, + { + title: 'real events recorded on bugs.chromium.org', + eventURL: + 'https://raw.githubusercontent.com/rrweb-io/benchmark-events/main/rrdom-benchmark-1.json', + times: 3, + }, ]; function avg(v: number[]): number { @@ -86,9 +94,6 @@ describe('benchmark: replayer fast-forward performance', () => { const bundlePath = path.resolve(__dirname, '../../dist/rrweb.min.js'); code = fs.readFileSync(bundlePath, 'utf8'); - - for (const suite of suites) - suite.eventsString = await generateEvents(suite.eval); }, 600_000); afterAll(async () => { @@ -99,6 +104,13 @@ describe('benchmark: replayer fast-forward performance', () => { it( suite.title, async () => { + if (suite.eval) suite.eventsString = await generateEvents(suite.eval); + else if (suite.eventURL) { + suite.eventsString = await fetchEventsWithCache( + suite.eventURL, + './temp', + ); + } else throw new Error('Invalid suite'); suite.times = suite.times ?? 5; const durations: number[] = []; for (let i = 0; i < suite.times; i++) { @@ -168,4 +180,37 @@ describe('benchmark: replayer fast-forward performance', () => { await page.close(); return eventsString; } + + /** + * Fetch the recorded events from URL. If the events are already cached, read from the cache. + */ + async function fetchEventsWithCache( + eventURL: string, + cacheFolder: string, + ): Promise { + const fileName = eventURL.split('/').pop() || ''; + const cachePath = path.resolve(__dirname, cacheFolder, fileName); + if (fs.existsSync(cachePath)) return fs.readFileSync(cachePath, 'utf8'); + return new Promise((resolve, reject) => { + https + .get(eventURL, (resp) => { + let data = ''; + resp.on('data', (chunk) => { + data += chunk; + }); + resp.on('end', () => { + resolve(data); + const folderAbsolutePath = path.resolve(__dirname, cacheFolder); + if (!fs.existsSync(folderAbsolutePath)) + fs.mkdirSync(path.resolve(__dirname, cacheFolder), { + recursive: true, + }); + fs.writeFileSync(cachePath, data); + }); + }) + .on('error', (err) => { + reject(err); + }); + }); + } }); From f9affc9747036fdca409ddc265e1b97e6b55eafc Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 14 Feb 2023 11:54:59 +0100 Subject: [PATCH 131/201] Chore: Bump up all package versions together whenever any one version changes (#1131) * Bump up all package versions together whenever any one version changes. Keeps everything in sync to make it easy to see if you have the correct dependencies. https://github.com/changesets/changesets/blob/main/docs/fixed-packages.md * Empty changeset as this changes nothing for the packages them selves * Bring @rrweb/web-extension version inline with other packages * separate the extension version from package.json's to avoid errors --------- Co-authored-by: Yun Feng --- .changeset/config.json | 12 +++++++++++- .changeset/five-peas-lay.md | 2 ++ packages/web-extension/package.json | 2 +- packages/web-extension/vite.config.ts | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 .changeset/five-peas-lay.md diff --git a/.changeset/config.json b/.changeset/config.json index 0b0f087c53..41a818e7ba 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,7 +2,17 @@ "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", "changelog": ["@changesets/changelog-github", { "repo": "rrweb-io/rrweb" }], "commit": false, - "fixed": [], + "fixed": [ + [ + "rrweb", + "rrweb-snapshot", + "rrdom", + "rrdom-nodejs", + "rrweb-player", + "@rrweb/types", + "@rrweb/web-extension" + ] + ], "linked": [], "access": "public", "baseBranch": "master", diff --git a/.changeset/five-peas-lay.md b/.changeset/five-peas-lay.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/five-peas-lay.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 54679add8a..b439c4be46 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0", + "version": "2.0.0-alpha.4", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index d46a8a0d43..75ee5b6394 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -66,7 +66,7 @@ export default defineConfig({ process.env.TARGET_BROWSER === 'chrome' ? 'chrome' : 'firefox'; const commonManifest = originalManifest.common; const manifest = { - version: packageJson.version, + version: '2.0.0', author: packageJson.author, version_name: packageJson.dependencies?.rrweb?.replace('^', ''), ...commonManifest, From b540c047c2a514db65eb3e61d3b211d652af7914 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Tue, 14 Feb 2023 06:06:59 -0500 Subject: [PATCH 132/201] fix: Exclude scripts loaded with `rel=modulepreload` from snapshots (#1128) * fix: Exclude `modulepreload` as well We were only excluding `` but we should include `rel="modulepreload"` as well * Apply formatting changes --- packages/rrweb-snapshot/src/rebuild.ts | 3 ++- packages/rrweb-snapshot/src/snapshot.ts | 5 +++-- .../test/__snapshots__/integration.test.ts.snap | 1 + packages/rrweb-snapshot/test/html/preload.html | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 854d6c957b..1582104a45 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -221,7 +221,8 @@ function buildNode( continue; } else if ( tagName === 'link' && - n.attributes.rel === 'preload' && + (n.attributes.rel === 'preload' || + n.attributes.rel === 'modulepreload') && n.attributes.as === 'script' ) { // ignore diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 99a23ff7be..e8078388a7 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -813,9 +813,10 @@ function slimDOMExcluded( slimDOMOptions.script && // script tag (sn.tagName === 'script' || - // preload link + // (module)preload link (sn.tagName === 'link' && - sn.attributes.rel === 'preload' && + (sn.attributes.rel === 'preload' || + sn.attributes.rel === 'modulepreload') && sn.attributes.as === 'script') || // prefetch link (sn.tagName === 'link' && diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 448823cbce..529a51eeff 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -370,6 +370,7 @@ exports[`integration tests [html file]: preload.html 1`] = ` Document + " `; diff --git a/packages/rrweb-snapshot/test/html/preload.html b/packages/rrweb-snapshot/test/html/preload.html index 32e84a260d..c160f60895 100644 --- a/packages/rrweb-snapshot/test/html/preload.html +++ b/packages/rrweb-snapshot/test/html/preload.html @@ -4,6 +4,7 @@ Document + From 39f8c24f1da5b0077d743b308e6592579c962ec4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 15:51:42 +0100 Subject: [PATCH 133/201] Version Packages (alpha) (#1123) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 14 +++++- packages/rrdom-nodejs/CHANGELOG.md | 18 ++++++++ packages/rrdom-nodejs/package.json | 6 +-- packages/rrdom/CHANGELOG.md | 19 ++++++++ packages/rrdom/package.json | 6 +-- packages/rrweb-player/CHANGELOG.md | 10 ++++ packages/rrweb-player/package.json | 6 +-- packages/rrweb-snapshot/CHANGELOG.md | 9 ++++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 69 ++++++++++++++++++++++++++++ packages/rrweb/package.json | 8 ++-- packages/types/CHANGELOG.md | 8 ++++ packages/types/package.json | 4 +- packages/web-extension/CHANGELOG.md | 11 +++++ packages/web-extension/package.json | 8 ++-- 15 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 packages/rrdom-nodejs/CHANGELOG.md create mode 100644 packages/rrdom/CHANGELOG.md create mode 100644 packages/rrweb-player/CHANGELOG.md create mode 100644 packages/rrweb-snapshot/CHANGELOG.md create mode 100644 packages/rrweb/CHANGELOG.md create mode 100644 packages/types/CHANGELOG.md create mode 100644 packages/web-extension/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 96dfc32c78..c8ea197df5 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -7,7 +7,17 @@ "rrweb": "2.0.0-alpha.4", "rrweb-player": "1.0.0-alpha.4", "rrweb-snapshot": "2.0.0-alpha.4", - "@rrweb/types": "2.0.0-alpha.4" + "@rrweb/types": "2.0.0-alpha.4", + "@rrweb/web-extension": "2.0.0-alpha.4" }, - "changesets": [] + "changesets": [ + "five-peas-lay", + "loud-seals-raise", + "lovely-pears-cross", + "nervous-poets-grin", + "real-trains-switch", + "serious-ants-juggle", + "tidy-yaks-joke", + "twenty-lies-switch" + ] } diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md new file mode 100644 index 0000000000..99b9cf823d --- /dev/null +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -0,0 +1,18 @@ +# rrdom-nodejs + +## 2.0.0-alpha.5 + +### Major Changes + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: + - rrweb-snapshot@2.0.0-alpha.5 + - rrdom@2.0.0-alpha.5 diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 8e947a6ce0..3a4ac396c1 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "0.1.7", + "version": "2.0.0-alpha.5", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,8 +49,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^0.1.7", - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrdom": "^2.0.0-alpha.5", + "rrweb-snapshot": "^2.0.0-alpha.5" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md new file mode 100644 index 0000000000..d0a0e25998 --- /dev/null +++ b/packages/rrdom/CHANGELOG.md @@ -0,0 +1,19 @@ +# rrdom + +## 2.0.0-alpha.5 + +### Major Changes + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: + - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index b9e125c10f..a124841fa0 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "0.1.7", + "version": "2.0.0-alpha.5", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.cjs", @@ -32,7 +32,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -48,6 +48,6 @@ "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrweb-snapshot": "^2.0.0-alpha.5" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md new file mode 100644 index 0000000000..2901994c1a --- /dev/null +++ b/packages/rrweb-player/CHANGELOG.md @@ -0,0 +1,10 @@ +# rrweb-player + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: + - rrweb@2.0.0-alpha.5 diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 93368119d2..7449f446e8 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "1.0.0-alpha.4", + "version": "2.0.0-alpha.5", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -25,7 +25,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.4" + "rrweb": "^2.0.0-alpha.5" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md new file mode 100644 index 0000000000..87838c2161 --- /dev/null +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -0,0 +1,9 @@ +# rrweb-snapshot + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 898209c5f5..bcf1c0c232 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md new file mode 100644 index 0000000000..7e0e373c23 --- /dev/null +++ b/packages/rrweb/CHANGELOG.md @@ -0,0 +1,69 @@ +# rrweb + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +- [#1121](https://github.com/rrweb-io/rrweb/pull/1121) [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: outdated ':hover' styles can't be removed from iframes or shadow doms + +- [#1122](https://github.com/rrweb-io/rrweb/pull/1122) [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add missing change logs manually. In the next version, all change logs will be generated automatically. + + - [`a220835`](https://github.com/rrweb-io/rrweb/commit/a220835eeb81ca4f294682e060d46c8853720d7f) [#1053](https://github.com/rrweb-io/rrweb/pull/1053) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Post message can break cross origin iframe recording. + + - [`7e8dcdb`](https://github.com/rrweb-io/rrweb/commit/7e8dcdb11dc5dfefcdd19ff5e13ec9d8b5c24dcc) [#1063](https://github.com/rrweb-io/rrweb/pull/1063) Thanks [@lele0108](https://github.com/lele0108)! - Fix: muted false -> true not being set. + + - [`b655361`](https://github.com/rrweb-io/rrweb/commit/b655361a5f0d50a053fcd0e5c823b8494c33b89c) [#1067](https://github.com/rrweb-io/rrweb/pull/1067) Thanks [@mydea](https://github.com/mydea)! - Export recordOptions type. + + - [`36b44e1`](https://github.com/rrweb-io/rrweb/commit/36b44e104b91fc74c3e69684111240cd23105340) [#1042](https://github.com/rrweb-io/rrweb/pull/1042) Thanks [@wfk007](https://github.com/wfk007)! - Fix: Failed to execute insertBefore on Node. + + - [`44e92cb`](https://github.com/rrweb-io/rrweb/commit/44e92cbff981c36e754dfcb9a184eae9e7292ecf) [#1058](https://github.com/rrweb-io/rrweb/pull/1058) Thanks [@mydea](https://github.com/mydea)! - Handle errors when observing iframes. + + - [`729b8bf`](https://github.com/rrweb-io/rrweb/commit/729b8bf38c8c7f2e1b22b4e0f7cab14f0807bc74) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Catch iframe manager & fix formatting issues. + + - [`03821d9`](https://github.com/rrweb-io/rrweb/commit/03821d9b9fa0513e6e373881d43102ceb9388340) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Harmonize on a single getWindowScroll + + - [`d08913d`](https://github.com/rrweb-io/rrweb/commit/d08913d0dc506dbf119e94686fe5f01c415316c9) [#1086](https://github.com/rrweb-io/rrweb/pull/1086) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: missed adopted style sheets of shadow doms in checkout full snapshot. + + - [`66abe17`](https://github.com/rrweb-io/rrweb/commit/66abe17832dbb23b3948af1c394f9a02caccc17b) [#1032](https://github.com/rrweb-io/rrweb/pull/1032) Thanks [@dbseel](https://github.com/dbseel)! - Fix: isBlocked throws on invalid HTML element. + + - [`07aa1b2`](https://github.com/rrweb-io/rrweb/commit/07aa1b2807da5a9a1db678ebc3ff59320a300d06) [#1049](https://github.com/rrweb-io/rrweb/pull/1049) Thanks [@Juice10](https://github.com/Juice10)! - Fix: shadow dom bugs. + + - [`57a2e14`](https://github.com/rrweb-io/rrweb/commit/57a2e140ea419f7790b1672529f21dfe2261b52b) [#1088](https://github.com/rrweb-io/rrweb/pull/1088) Thanks [@mydea](https://github.com/mydea)! - Fix: Guard against missing window.CSSStyleSheet. + + - [`fc82869`](https://github.com/rrweb-io/rrweb/commit/fc828694099b87b4d811e6b651a7bb4c7499b896) [#1093](https://github.com/rrweb-io/rrweb/pull/1093) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: cross origin iframe bugs. + + - [`a77e302`](https://github.com/rrweb-io/rrweb/commit/a77e30217893e63f8025c73afc3ac1ba294d7761) [#1104](https://github.com/rrweb-io/rrweb/pull/1104) Thanks [@jlalmes](https://github.com/jlalmes)! - [console-plugin] Feat: Record unhandled rejection event. + + - [`25a4f5a`](https://github.com/rrweb-io/rrweb/commit/25a4f5ab6c7311f2e8e5e1a4d232c2820adf910e) [#1115](https://github.com/rrweb-io/rrweb/pull/1115) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Don't trigger Finish event when in liveMode. + + - [`cb15800`](https://github.com/rrweb-io/rrweb/commit/cb1580008d04b0bc5c5d4ebec0e2e79899faaeb6) [#1106](https://github.com/rrweb-io/rrweb/pull/1106) Thanks [@mydea](https://github.com/mydea)! - Fix: Ensure CSS support is checked more robustly. + + - [`0732618`](https://github.com/rrweb-io/rrweb/commit/07326182f9750646771918481f116b946a17c2a9) [#1100](https://github.com/rrweb-io/rrweb/pull/1100) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: wrong rootId value in special iframes. + + - [`3caa25e`](https://github.com/rrweb-io/rrweb/commit/3caa25ed9b19954c98775f22d5fa47233fa3d1db) [#1098](https://github.com/rrweb-io/rrweb/pull/1098) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Refactor: Don't have requestAnimationFrame looping in background for Live Mode. + + - [`3a26e36`](https://github.com/rrweb-io/rrweb/commit/3a26e36f6f625c0391c7e6d3f1050660adfccc4f) [#1092](https://github.com/rrweb-io/rrweb/pull/1092) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: regression of issue: ShadowHost can't be a string (issue 941) + + - [`07d22e7`](https://github.com/rrweb-io/rrweb/commit/07d22e7cd999a48e7371aaef1b979574bb746500) [#1111](https://github.com/rrweb-io/rrweb/pull/1111) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: enable to customize logger in the replayer. + + - [`0627d4d`](https://github.com/rrweb-io/rrweb/commit/0627d4df7cc76cde7babbd37ab8e3da5810fb51d) [#1109](https://github.com/rrweb-io/rrweb/pull/1109) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: add option to record on DOMContentLoaded event. + + - [`174b9ac`](https://github.com/rrweb-io/rrweb/commit/174b9ac066565b8c065f40f0303189f10c7c4efb) [#1112](https://github.com/rrweb-io/rrweb/pull/1112) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed. + + - [`5a1e5e9`](https://github.com/rrweb-io/rrweb/commit/5a1e5e919e3f8bef48d142115c0afd5706a442b5) [#1119](https://github.com/rrweb-io/rrweb/pull/1119) Thanks [@Juice10](https://github.com/Juice10)! - Feat: Automate NPM package releases. + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: + - rrweb-snapshot@2.0.0-alpha.5 + - rrdom@2.0.0-alpha.5 + - @rrweb/types@2.0.0-alpha.5 diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index f0b9bc4232..7455c83554 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -78,13 +78,13 @@ "typescript": "^4.7.3" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^0.1.7", - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrdom": "^2.0.0-alpha.5", + "rrweb-snapshot": "^2.0.0-alpha.5" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md new file mode 100644 index 0000000000..8eb542bd3c --- /dev/null +++ b/packages/types/CHANGELOG.md @@ -0,0 +1,8 @@ +# @rrweb/types + +## 2.0.0-alpha.5 + +### Patch Changes + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: + - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/types/package.json b/packages/types/package.json index a6060cd16f..eae22c613d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "publishConfig": { "access": "public" }, @@ -44,7 +44,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrweb-snapshot": "^2.0.0-alpha.5" }, "browserslist": [ "supports es6-class" diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md new file mode 100644 index 0000000000..b00e2b7d4b --- /dev/null +++ b/packages/web-extension/CHANGELOG.md @@ -0,0 +1,11 @@ +# @rrweb/web-extension + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1044](https://github.com/rrweb-io/rrweb/pull/1044) [`282c8fa`](https://github.com/rrweb-io/rrweb/commit/282c8fa415318e56e8b63fa30ce3f173813b39c3) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add rrweb browser extension + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: + - rrweb@2.0.0-alpha.5 + - rrweb-player@2.0.0-alpha.5 diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index b439c4be46..578c95a7c6 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -16,7 +16,7 @@ "prepublish": "npm run pack:chrome && npm run pack:firefox" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^2.1.0", @@ -41,7 +41,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.4", - "rrweb-player": "^1.0.0-alpha.4" + "rrweb": "^2.0.0-alpha.5", + "rrweb-player": "^2.0.0-alpha.5" } } From 28111c14b1120a1f145f95bc1ab8ef3a36b27f23 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 14 Feb 2023 22:39:23 +0100 Subject: [PATCH 134/201] Fix broken changeset release action (#1134) * Fix broken changeset release action * Create mighty-frogs-sparkle.md --- .changeset/mighty-frogs-sparkle.md | 2 ++ .github/workflows/release.yml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .changeset/mighty-frogs-sparkle.md diff --git a/.changeset/mighty-frogs-sparkle.md b/.changeset/mighty-frogs-sparkle.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/mighty-frogs-sparkle.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f9688d18a..1e895be580 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,8 +27,9 @@ jobs: id: changesets uses: changesets/action@v1 with: - publish: NODE_OPTIONS='--max-old-space-size=4096' yarn run release + publish: yarn run release env: + NODE_OPTIONS: "--max-old-space-size=4096" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 033d2d18b42444f578ddc0822695aaf43ee6b9f2 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 15 Feb 2023 09:32:43 +1100 Subject: [PATCH 135/201] some minor changes for releasing the extension and revert failed publish PR #1123 (#1132) * improve extension bundle * Revert "Version Packages (alpha) (#1123)" This reverts commit 39f8c24f1da5b0077d743b308e6592579c962ec4. --- .changeset/pre.json | 14 +--- packages/rrdom-nodejs/CHANGELOG.md | 18 ----- packages/rrdom-nodejs/package.json | 6 +- packages/rrdom/CHANGELOG.md | 19 ------ packages/rrdom/package.json | 6 +- packages/rrweb-player/CHANGELOG.md | 10 --- packages/rrweb-player/package.json | 6 +- packages/rrweb-snapshot/CHANGELOG.md | 9 --- packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 69 -------------------- packages/rrweb/package.json | 8 +-- packages/types/CHANGELOG.md | 8 --- packages/types/package.json | 4 +- packages/web-extension/CHANGELOG.md | 11 ---- packages/web-extension/package.json | 10 +-- packages/web-extension/src/content/index.ts | 6 +- packages/web-extension/src/content/inject.ts | 8 ++- packages/web-extension/src/utils/channel.ts | 15 ++++- packages/web-extension/vite.config.ts | 7 +- yarn.lock | 19 ++---- 20 files changed, 56 insertions(+), 199 deletions(-) delete mode 100644 packages/rrdom-nodejs/CHANGELOG.md delete mode 100644 packages/rrdom/CHANGELOG.md delete mode 100644 packages/rrweb-player/CHANGELOG.md delete mode 100644 packages/rrweb-snapshot/CHANGELOG.md delete mode 100644 packages/rrweb/CHANGELOG.md delete mode 100644 packages/types/CHANGELOG.md delete mode 100644 packages/web-extension/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json index c8ea197df5..96dfc32c78 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -7,17 +7,7 @@ "rrweb": "2.0.0-alpha.4", "rrweb-player": "1.0.0-alpha.4", "rrweb-snapshot": "2.0.0-alpha.4", - "@rrweb/types": "2.0.0-alpha.4", - "@rrweb/web-extension": "2.0.0-alpha.4" + "@rrweb/types": "2.0.0-alpha.4" }, - "changesets": [ - "five-peas-lay", - "loud-seals-raise", - "lovely-pears-cross", - "nervous-poets-grin", - "real-trains-switch", - "serious-ants-juggle", - "tidy-yaks-joke", - "twenty-lies-switch" - ] + "changesets": [] } diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md deleted file mode 100644 index 99b9cf823d..0000000000 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ /dev/null @@ -1,18 +0,0 @@ -# rrdom-nodejs - -## 2.0.0-alpha.5 - -### Major Changes - -- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. - - 1. Refactor: change the data structure of childNodes from array to linked list - 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) - -### Patch Changes - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: - - rrweb-snapshot@2.0.0-alpha.5 - - rrdom@2.0.0-alpha.5 diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 3a4ac396c1..8e947a6ce0 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "2.0.0-alpha.5", + "version": "0.1.7", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,8 +49,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^2.0.0-alpha.5", - "rrweb-snapshot": "^2.0.0-alpha.5" + "rrdom": "^0.1.7", + "rrweb-snapshot": "^2.0.0-alpha.4" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md deleted file mode 100644 index d0a0e25998..0000000000 --- a/packages/rrdom/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -# rrdom - -## 2.0.0-alpha.5 - -### Major Changes - -- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. - - 1. Refactor: change the data structure of childNodes from array to linked list - 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) - -### Patch Changes - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: - - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index a124841fa0..b9e125c10f 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "2.0.0-alpha.5", + "version": "0.1.7", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.cjs", @@ -32,7 +32,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rrweb/types": "^2.0.0-alpha.5", + "@rrweb/types": "^2.0.0-alpha.4", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -48,6 +48,6 @@ "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.5" + "rrweb-snapshot": "^2.0.0-alpha.4" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md deleted file mode 100644 index 2901994c1a..0000000000 --- a/packages/rrweb-player/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# rrweb-player - -## 2.0.0-alpha.5 - -### Patch Changes - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: - - rrweb@2.0.0-alpha.5 diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 7449f446e8..93368119d2 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "2.0.0-alpha.5", + "version": "1.0.0-alpha.4", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.5", + "@rrweb/types": "^2.0.0-alpha.4", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -25,7 +25,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.5" + "rrweb": "^2.0.0-alpha.4" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md deleted file mode 100644 index 87838c2161..0000000000 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# rrweb-snapshot - -## 2.0.0-alpha.5 - -### Patch Changes - -- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index bcf1c0c232..898209c5f5 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.4", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md deleted file mode 100644 index 7e0e373c23..0000000000 --- a/packages/rrweb/CHANGELOG.md +++ /dev/null @@ -1,69 +0,0 @@ -# rrweb - -## 2.0.0-alpha.5 - -### Patch Changes - -- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). - -- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). - -- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. - - 1. Refactor: change the data structure of childNodes from array to linked list - 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) - -- [#1121](https://github.com/rrweb-io/rrweb/pull/1121) [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: outdated ':hover' styles can't be removed from iframes or shadow doms - -- [#1122](https://github.com/rrweb-io/rrweb/pull/1122) [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add missing change logs manually. In the next version, all change logs will be generated automatically. - - - [`a220835`](https://github.com/rrweb-io/rrweb/commit/a220835eeb81ca4f294682e060d46c8853720d7f) [#1053](https://github.com/rrweb-io/rrweb/pull/1053) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Post message can break cross origin iframe recording. - - - [`7e8dcdb`](https://github.com/rrweb-io/rrweb/commit/7e8dcdb11dc5dfefcdd19ff5e13ec9d8b5c24dcc) [#1063](https://github.com/rrweb-io/rrweb/pull/1063) Thanks [@lele0108](https://github.com/lele0108)! - Fix: muted false -> true not being set. - - - [`b655361`](https://github.com/rrweb-io/rrweb/commit/b655361a5f0d50a053fcd0e5c823b8494c33b89c) [#1067](https://github.com/rrweb-io/rrweb/pull/1067) Thanks [@mydea](https://github.com/mydea)! - Export recordOptions type. - - - [`36b44e1`](https://github.com/rrweb-io/rrweb/commit/36b44e104b91fc74c3e69684111240cd23105340) [#1042](https://github.com/rrweb-io/rrweb/pull/1042) Thanks [@wfk007](https://github.com/wfk007)! - Fix: Failed to execute insertBefore on Node. - - - [`44e92cb`](https://github.com/rrweb-io/rrweb/commit/44e92cbff981c36e754dfcb9a184eae9e7292ecf) [#1058](https://github.com/rrweb-io/rrweb/pull/1058) Thanks [@mydea](https://github.com/mydea)! - Handle errors when observing iframes. - - - [`729b8bf`](https://github.com/rrweb-io/rrweb/commit/729b8bf38c8c7f2e1b22b4e0f7cab14f0807bc74) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Catch iframe manager & fix formatting issues. - - - [`03821d9`](https://github.com/rrweb-io/rrweb/commit/03821d9b9fa0513e6e373881d43102ceb9388340) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Harmonize on a single getWindowScroll - - - [`d08913d`](https://github.com/rrweb-io/rrweb/commit/d08913d0dc506dbf119e94686fe5f01c415316c9) [#1086](https://github.com/rrweb-io/rrweb/pull/1086) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: missed adopted style sheets of shadow doms in checkout full snapshot. - - - [`66abe17`](https://github.com/rrweb-io/rrweb/commit/66abe17832dbb23b3948af1c394f9a02caccc17b) [#1032](https://github.com/rrweb-io/rrweb/pull/1032) Thanks [@dbseel](https://github.com/dbseel)! - Fix: isBlocked throws on invalid HTML element. - - - [`07aa1b2`](https://github.com/rrweb-io/rrweb/commit/07aa1b2807da5a9a1db678ebc3ff59320a300d06) [#1049](https://github.com/rrweb-io/rrweb/pull/1049) Thanks [@Juice10](https://github.com/Juice10)! - Fix: shadow dom bugs. - - - [`57a2e14`](https://github.com/rrweb-io/rrweb/commit/57a2e140ea419f7790b1672529f21dfe2261b52b) [#1088](https://github.com/rrweb-io/rrweb/pull/1088) Thanks [@mydea](https://github.com/mydea)! - Fix: Guard against missing window.CSSStyleSheet. - - - [`fc82869`](https://github.com/rrweb-io/rrweb/commit/fc828694099b87b4d811e6b651a7bb4c7499b896) [#1093](https://github.com/rrweb-io/rrweb/pull/1093) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: cross origin iframe bugs. - - - [`a77e302`](https://github.com/rrweb-io/rrweb/commit/a77e30217893e63f8025c73afc3ac1ba294d7761) [#1104](https://github.com/rrweb-io/rrweb/pull/1104) Thanks [@jlalmes](https://github.com/jlalmes)! - [console-plugin] Feat: Record unhandled rejection event. - - - [`25a4f5a`](https://github.com/rrweb-io/rrweb/commit/25a4f5ab6c7311f2e8e5e1a4d232c2820adf910e) [#1115](https://github.com/rrweb-io/rrweb/pull/1115) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Don't trigger Finish event when in liveMode. - - - [`cb15800`](https://github.com/rrweb-io/rrweb/commit/cb1580008d04b0bc5c5d4ebec0e2e79899faaeb6) [#1106](https://github.com/rrweb-io/rrweb/pull/1106) Thanks [@mydea](https://github.com/mydea)! - Fix: Ensure CSS support is checked more robustly. - - - [`0732618`](https://github.com/rrweb-io/rrweb/commit/07326182f9750646771918481f116b946a17c2a9) [#1100](https://github.com/rrweb-io/rrweb/pull/1100) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: wrong rootId value in special iframes. - - - [`3caa25e`](https://github.com/rrweb-io/rrweb/commit/3caa25ed9b19954c98775f22d5fa47233fa3d1db) [#1098](https://github.com/rrweb-io/rrweb/pull/1098) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Refactor: Don't have requestAnimationFrame looping in background for Live Mode. - - - [`3a26e36`](https://github.com/rrweb-io/rrweb/commit/3a26e36f6f625c0391c7e6d3f1050660adfccc4f) [#1092](https://github.com/rrweb-io/rrweb/pull/1092) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: regression of issue: ShadowHost can't be a string (issue 941) - - - [`07d22e7`](https://github.com/rrweb-io/rrweb/commit/07d22e7cd999a48e7371aaef1b979574bb746500) [#1111](https://github.com/rrweb-io/rrweb/pull/1111) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: enable to customize logger in the replayer. - - - [`0627d4d`](https://github.com/rrweb-io/rrweb/commit/0627d4df7cc76cde7babbd37ab8e3da5810fb51d) [#1109](https://github.com/rrweb-io/rrweb/pull/1109) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: add option to record on DOMContentLoaded event. - - - [`174b9ac`](https://github.com/rrweb-io/rrweb/commit/174b9ac066565b8c065f40f0303189f10c7c4efb) [#1112](https://github.com/rrweb-io/rrweb/pull/1112) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed. - - - [`5a1e5e9`](https://github.com/rrweb-io/rrweb/commit/5a1e5e919e3f8bef48d142115c0afd5706a442b5) [#1119](https://github.com/rrweb-io/rrweb/pull/1119) Thanks [@Juice10](https://github.com/Juice10)! - Feat: Automate NPM package releases. - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: - - rrweb-snapshot@2.0.0-alpha.5 - - rrdom@2.0.0-alpha.5 - - @rrweb/types@2.0.0-alpha.5 diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 7455c83554..f0b9bc4232 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.4", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -78,13 +78,13 @@ "typescript": "^4.7.3" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.5", + "@rrweb/types": "^2.0.0-alpha.4", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^2.0.0-alpha.5", - "rrweb-snapshot": "^2.0.0-alpha.5" + "rrdom": "^0.1.7", + "rrweb-snapshot": "^2.0.0-alpha.4" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md deleted file mode 100644 index 8eb542bd3c..0000000000 --- a/packages/types/CHANGELOG.md +++ /dev/null @@ -1,8 +0,0 @@ -# @rrweb/types - -## 2.0.0-alpha.5 - -### Patch Changes - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: - - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/types/package.json b/packages/types/package.json index eae22c613d..a6060cd16f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.4", "publishConfig": { "access": "public" }, @@ -44,7 +44,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.5" + "rrweb-snapshot": "^2.0.0-alpha.4" }, "browserslist": [ "supports es6-class" diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md deleted file mode 100644 index b00e2b7d4b..0000000000 --- a/packages/web-extension/CHANGELOG.md +++ /dev/null @@ -1,11 +0,0 @@ -# @rrweb/web-extension - -## 2.0.0-alpha.5 - -### Patch Changes - -- [#1044](https://github.com/rrweb-io/rrweb/pull/1044) [`282c8fa`](https://github.com/rrweb-io/rrweb/commit/282c8fa415318e56e8b63fa30ce3f173813b39c3) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add rrweb browser extension - -- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: - - rrweb@2.0.0-alpha.5 - - rrweb-player@2.0.0-alpha.5 diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 578c95a7c6..0722339880 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0-alpha.5", + "version": "2.0.0-alpha.4", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -16,7 +16,7 @@ "prepublish": "npm run pack:chrome && npm run pack:firefox" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.5", + "@rrweb/types": "^2.0.0-alpha.4", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^2.1.0", @@ -25,7 +25,7 @@ "typescript": "^4.7.3", "vite": "^3.1.8", "vite-plugin-web-extension": "^1.4.5", - "vite-plugin-zip": "^1.0.1", + "vite-plugin-zip-pack": "^1.0.5", "webextension-polyfill": "^0.10.0" }, "dependencies": { @@ -41,7 +41,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.5", - "rrweb-player": "^2.0.0-alpha.5" + "rrweb": "^2.0.0-alpha.4", + "rrweb-player": "^1.0.0-alpha.4" } } diff --git a/packages/web-extension/src/content/index.ts b/packages/web-extension/src/content/index.ts index 0fec24b154..5f99e974aa 100644 --- a/packages/web-extension/src/content/index.ts +++ b/packages/web-extension/src/content/index.ts @@ -40,7 +40,7 @@ void (() => { ); if (isInCrossOriginIFrame()) { void initCrossOriginIframe(); - } else { + } else if (window === window.top) { void initMainPage(); } })(); @@ -154,7 +154,9 @@ async function initMainPage() { } // Before unload pages, cache the new events in the local storage. - window.addEventListener('beforeunload', () => { + window.addEventListener('beforeunload', (event) => { + event.preventDefault(); + if (newEvents.length === 0) return; void Browser.storage.local.set({ [LocalDataKey.bufferedEvents]: bufferedEvents.concat(newEvents), }); diff --git a/packages/web-extension/src/content/inject.ts b/packages/web-extension/src/content/inject.ts index 0f2d7d3f67..1d18474a01 100644 --- a/packages/web-extension/src/content/inject.ts +++ b/packages/web-extension/src/content/inject.ts @@ -43,7 +43,13 @@ const messageHandler = ( startRecord(data.config || {}); }, [MessageName.StopRecord]: () => { - if (stopFn) stopFn(); + if (stopFn) { + try { + stopFn(); + } catch (e) { + // + } + } postMessage({ message: MessageName.RecordStopped, events, diff --git a/packages/web-extension/src/utils/channel.ts b/packages/web-extension/src/utils/channel.ts index a1c0ea8eb7..4268811eac 100644 --- a/packages/web-extension/src/utils/channel.ts +++ b/packages/web-extension/src/utils/channel.ts @@ -130,11 +130,20 @@ class Channel { * @param eventName - event name * @param handler - event handler, accepts two arguments: * detail: event detail - * source: source of the event, chrome.runtime.MessageSender object + * source: source of the event, Browser.runtime.MessageSender object * @returns a function to remove the handler */ - public on(event: string, handler: (detail: unknown) => unknown) { - return this.emitter.on(event, handler); + public on( + event: string, + handler: (detail: unknown, sender: Runtime.MessageSender) => unknown, + ) { + const emitHandler = ((data: { + detail: unknown; + sender: Runtime.MessageSender; + }) => { + handler(data.detail, data.sender); + }) as (data: unknown) => unknown; + return this.emitter.on(event, emitHandler); } /** diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index 75ee5b6394..d676e0e608 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -5,7 +5,7 @@ import { PluginOption, } from 'vite'; import webExtension, { readJsonFile } from 'vite-plugin-web-extension'; -import zip from 'vite-plugin-zip'; +import zip from 'vite-plugin-zip-pack'; import * as path from 'path'; import type { PackageJson } from 'type-fest'; import react from '@vitejs/plugin-react'; @@ -94,8 +94,9 @@ export default defineConfig({ ), process.env.ZIP === 'true' && zip({ - dir: 'dist', - outputName: process.env.TARGET_BROWSER, + inDir: `dist/${process.env.TARGET_BROWSER || 'chrome'}`, + outDir: 'dist', + outFileName: `${process.env.TARGET_BROWSER || 'chrome'}.zip`, }), ], resolve: { diff --git a/yarn.lock b/yarn.lock index 0cf635e32d..bd7f23199f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10513,7 +10513,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jszip@^3.2.2: +jszip@^3.2.2, jszip@^3.7.1: version "3.10.1" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== @@ -15643,12 +15643,12 @@ vite-plugin-web-extension@^1.4.5: md5 "^2.3.0" web-ext "^6.5.0" -vite-plugin-zip@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vite-plugin-zip/-/vite-plugin-zip-1.0.1.tgz#a72ad7f29ddc873f7d6f3917397fd008a6badaa3" - integrity sha512-DdQQGq8eSLwMHtFmzwKZ/h59qaeSNsSFM6TKiUsHl2Cx2D1KD0iJD9OQP7rLOSEZCPoUoUvW45G2nJNcr0qlEg== +vite-plugin-zip-pack@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vite-plugin-zip-pack/-/vite-plugin-zip-pack-1.0.5.tgz#6c2ef0a02cdfc9c7a9c785a4310ec7024695c91f" + integrity sha512-AY6F3GJL///Dc3d7fQhalJvWb08e0oIATVqHLwDAiK2/S1NCxbAFXfT4m2V+LtJRjHrRkHa0zbZvAI6/lb6vTQ== dependencies: - yazl "^2.5.1" + jszip "^3.7.1" vite@^3.1.8: version "3.1.8" @@ -16151,13 +16151,6 @@ yauzl@2.10.0, yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -yazl@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" - integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== - dependencies: - buffer-crc32 "~0.2.3" - yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" From 06031a64868b5f23338b2276b23a0ccb7df110fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 06:37:19 +0800 Subject: [PATCH 136/201] Version Packages (alpha) (#1135) --- .changeset/pre.json | 15 +++++- packages/rrdom-nodejs/CHANGELOG.md | 18 ++++++++ packages/rrdom-nodejs/package.json | 6 +-- packages/rrdom/CHANGELOG.md | 19 ++++++++ packages/rrdom/package.json | 6 +-- packages/rrweb-player/CHANGELOG.md | 10 ++++ packages/rrweb-player/package.json | 6 +-- packages/rrweb-snapshot/CHANGELOG.md | 9 ++++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 69 ++++++++++++++++++++++++++++ packages/rrweb/package.json | 8 ++-- packages/types/CHANGELOG.md | 8 ++++ packages/types/package.json | 4 +- packages/web-extension/CHANGELOG.md | 11 +++++ packages/web-extension/package.json | 8 ++-- 15 files changed, 177 insertions(+), 22 deletions(-) create mode 100644 packages/rrdom-nodejs/CHANGELOG.md create mode 100644 packages/rrdom/CHANGELOG.md create mode 100644 packages/rrweb-player/CHANGELOG.md create mode 100644 packages/rrweb-snapshot/CHANGELOG.md create mode 100644 packages/rrweb/CHANGELOG.md create mode 100644 packages/types/CHANGELOG.md create mode 100644 packages/web-extension/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 96dfc32c78..8c739ee379 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -7,7 +7,18 @@ "rrweb": "2.0.0-alpha.4", "rrweb-player": "1.0.0-alpha.4", "rrweb-snapshot": "2.0.0-alpha.4", - "@rrweb/types": "2.0.0-alpha.4" + "@rrweb/types": "2.0.0-alpha.4", + "@rrweb/web-extension": "2.0.0-alpha.4" }, - "changesets": [] + "changesets": [ + "five-peas-lay", + "loud-seals-raise", + "lovely-pears-cross", + "mighty-frogs-sparkle", + "nervous-poets-grin", + "real-trains-switch", + "serious-ants-juggle", + "tidy-yaks-joke", + "twenty-lies-switch" + ] } diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md new file mode 100644 index 0000000000..99b9cf823d --- /dev/null +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -0,0 +1,18 @@ +# rrdom-nodejs + +## 2.0.0-alpha.5 + +### Major Changes + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: + - rrweb-snapshot@2.0.0-alpha.5 + - rrdom@2.0.0-alpha.5 diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 8e947a6ce0..3a4ac396c1 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "0.1.7", + "version": "2.0.0-alpha.5", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -49,8 +49,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^0.1.7", - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrdom": "^2.0.0-alpha.5", + "rrweb-snapshot": "^2.0.0-alpha.5" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md new file mode 100644 index 0000000000..d0a0e25998 --- /dev/null +++ b/packages/rrdom/CHANGELOG.md @@ -0,0 +1,19 @@ +# rrdom + +## 2.0.0-alpha.5 + +### Major Changes + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: + - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index b9e125c10f..a124841fa0 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "0.1.7", + "version": "2.0.0-alpha.5", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.cjs", @@ -32,7 +32,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -48,6 +48,6 @@ "typescript": "^4.7.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrweb-snapshot": "^2.0.0-alpha.5" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md new file mode 100644 index 0000000000..2901994c1a --- /dev/null +++ b/packages/rrweb-player/CHANGELOG.md @@ -0,0 +1,10 @@ +# rrweb-player + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: + - rrweb@2.0.0-alpha.5 diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 93368119d2..7449f446e8 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "1.0.0-alpha.4", + "version": "2.0.0-alpha.5", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -25,7 +25,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.4" + "rrweb": "^2.0.0-alpha.5" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md new file mode 100644 index 0000000000..87838c2161 --- /dev/null +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -0,0 +1,9 @@ +# rrweb-snapshot + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 898209c5f5..bcf1c0c232 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md new file mode 100644 index 0000000000..7e0e373c23 --- /dev/null +++ b/packages/rrweb/CHANGELOG.md @@ -0,0 +1,69 @@ +# rrweb + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1095](https://github.com/rrweb-io/rrweb/pull/1095) [`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix duplicated shadow doms + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor all suffix of bundled scripts with commonjs module from 'js' to cjs [#1087](https://github.com/rrweb-io/rrweb/pull/1087). + +- [#1126](https://github.com/rrweb-io/rrweb/pull/1126) [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: improve rrdom robustness [#1091](https://github.com/rrweb-io/rrweb/pull/1091). + +- [#1127](https://github.com/rrweb-io/rrweb/pull/1127) [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve performance by 80% in a super large benchmark case. + + 1. Refactor: change the data structure of childNodes from array to linked list + 2. Improve the performance of the "contains" function. New algorithm will reduce the complexity from O(n) to O(logn) + +- [#1121](https://github.com/rrweb-io/rrweb/pull/1121) [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: outdated ':hover' styles can't be removed from iframes or shadow doms + +- [#1122](https://github.com/rrweb-io/rrweb/pull/1122) [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add missing change logs manually. In the next version, all change logs will be generated automatically. + + - [`a220835`](https://github.com/rrweb-io/rrweb/commit/a220835eeb81ca4f294682e060d46c8853720d7f) [#1053](https://github.com/rrweb-io/rrweb/pull/1053) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Post message can break cross origin iframe recording. + + - [`7e8dcdb`](https://github.com/rrweb-io/rrweb/commit/7e8dcdb11dc5dfefcdd19ff5e13ec9d8b5c24dcc) [#1063](https://github.com/rrweb-io/rrweb/pull/1063) Thanks [@lele0108](https://github.com/lele0108)! - Fix: muted false -> true not being set. + + - [`b655361`](https://github.com/rrweb-io/rrweb/commit/b655361a5f0d50a053fcd0e5c823b8494c33b89c) [#1067](https://github.com/rrweb-io/rrweb/pull/1067) Thanks [@mydea](https://github.com/mydea)! - Export recordOptions type. + + - [`36b44e1`](https://github.com/rrweb-io/rrweb/commit/36b44e104b91fc74c3e69684111240cd23105340) [#1042](https://github.com/rrweb-io/rrweb/pull/1042) Thanks [@wfk007](https://github.com/wfk007)! - Fix: Failed to execute insertBefore on Node. + + - [`44e92cb`](https://github.com/rrweb-io/rrweb/commit/44e92cbff981c36e754dfcb9a184eae9e7292ecf) [#1058](https://github.com/rrweb-io/rrweb/pull/1058) Thanks [@mydea](https://github.com/mydea)! - Handle errors when observing iframes. + + - [`729b8bf`](https://github.com/rrweb-io/rrweb/commit/729b8bf38c8c7f2e1b22b4e0f7cab14f0807bc74) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Catch iframe manager & fix formatting issues. + + - [`03821d9`](https://github.com/rrweb-io/rrweb/commit/03821d9b9fa0513e6e373881d43102ceb9388340) [#1083](https://github.com/rrweb-io/rrweb/pull/1083) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Harmonize on a single getWindowScroll + + - [`d08913d`](https://github.com/rrweb-io/rrweb/commit/d08913d0dc506dbf119e94686fe5f01c415316c9) [#1086](https://github.com/rrweb-io/rrweb/pull/1086) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: missed adopted style sheets of shadow doms in checkout full snapshot. + + - [`66abe17`](https://github.com/rrweb-io/rrweb/commit/66abe17832dbb23b3948af1c394f9a02caccc17b) [#1032](https://github.com/rrweb-io/rrweb/pull/1032) Thanks [@dbseel](https://github.com/dbseel)! - Fix: isBlocked throws on invalid HTML element. + + - [`07aa1b2`](https://github.com/rrweb-io/rrweb/commit/07aa1b2807da5a9a1db678ebc3ff59320a300d06) [#1049](https://github.com/rrweb-io/rrweb/pull/1049) Thanks [@Juice10](https://github.com/Juice10)! - Fix: shadow dom bugs. + + - [`57a2e14`](https://github.com/rrweb-io/rrweb/commit/57a2e140ea419f7790b1672529f21dfe2261b52b) [#1088](https://github.com/rrweb-io/rrweb/pull/1088) Thanks [@mydea](https://github.com/mydea)! - Fix: Guard against missing window.CSSStyleSheet. + + - [`fc82869`](https://github.com/rrweb-io/rrweb/commit/fc828694099b87b4d811e6b651a7bb4c7499b896) [#1093](https://github.com/rrweb-io/rrweb/pull/1093) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: cross origin iframe bugs. + + - [`a77e302`](https://github.com/rrweb-io/rrweb/commit/a77e30217893e63f8025c73afc3ac1ba294d7761) [#1104](https://github.com/rrweb-io/rrweb/pull/1104) Thanks [@jlalmes](https://github.com/jlalmes)! - [console-plugin] Feat: Record unhandled rejection event. + + - [`25a4f5a`](https://github.com/rrweb-io/rrweb/commit/25a4f5ab6c7311f2e8e5e1a4d232c2820adf910e) [#1115](https://github.com/rrweb-io/rrweb/pull/1115) Thanks [@Juice10](https://github.com/Juice10)! - Fix: Don't trigger Finish event when in liveMode. + + - [`cb15800`](https://github.com/rrweb-io/rrweb/commit/cb1580008d04b0bc5c5d4ebec0e2e79899faaeb6) [#1106](https://github.com/rrweb-io/rrweb/pull/1106) Thanks [@mydea](https://github.com/mydea)! - Fix: Ensure CSS support is checked more robustly. + + - [`0732618`](https://github.com/rrweb-io/rrweb/commit/07326182f9750646771918481f116b946a17c2a9) [#1100](https://github.com/rrweb-io/rrweb/pull/1100) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: wrong rootId value in special iframes. + + - [`3caa25e`](https://github.com/rrweb-io/rrweb/commit/3caa25ed9b19954c98775f22d5fa47233fa3d1db) [#1098](https://github.com/rrweb-io/rrweb/pull/1098) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Refactor: Don't have requestAnimationFrame looping in background for Live Mode. + + - [`3a26e36`](https://github.com/rrweb-io/rrweb/commit/3a26e36f6f625c0391c7e6d3f1050660adfccc4f) [#1092](https://github.com/rrweb-io/rrweb/pull/1092) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: regression of issue: ShadowHost can't be a string (issue 941) + + - [`07d22e7`](https://github.com/rrweb-io/rrweb/commit/07d22e7cd999a48e7371aaef1b979574bb746500) [#1111](https://github.com/rrweb-io/rrweb/pull/1111) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: enable to customize logger in the replayer. + + - [`0627d4d`](https://github.com/rrweb-io/rrweb/commit/0627d4df7cc76cde7babbd37ab8e3da5810fb51d) [#1109](https://github.com/rrweb-io/rrweb/pull/1109) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Feat: add option to record on DOMContentLoaded event. + + - [`174b9ac`](https://github.com/rrweb-io/rrweb/commit/174b9ac066565b8c065f40f0303189f10c7c4efb) [#1112](https://github.com/rrweb-io/rrweb/pull/1112) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: mutation Failed to execute 'insertBefore' on 'Node': Only one doctype on document allowed. + + - [`5a1e5e9`](https://github.com/rrweb-io/rrweb/commit/5a1e5e919e3f8bef48d142115c0afd5706a442b5) [#1119](https://github.com/rrweb-io/rrweb/pull/1119) Thanks [@Juice10](https://github.com/Juice10)! - Feat: Automate NPM package releases. + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f)]: + - rrweb-snapshot@2.0.0-alpha.5 + - rrdom@2.0.0-alpha.5 + - @rrweb/types@2.0.0-alpha.5 diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index f0b9bc4232..7455c83554 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -78,13 +78,13 @@ "typescript": "^4.7.3" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^0.1.7", - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrdom": "^2.0.0-alpha.5", + "rrweb-snapshot": "^2.0.0-alpha.5" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md new file mode 100644 index 0000000000..8eb542bd3c --- /dev/null +++ b/packages/types/CHANGELOG.md @@ -0,0 +1,8 @@ +# @rrweb/types + +## 2.0.0-alpha.5 + +### Patch Changes + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff)]: + - rrweb-snapshot@2.0.0-alpha.5 diff --git a/packages/types/package.json b/packages/types/package.json index a6060cd16f..eae22c613d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "publishConfig": { "access": "public" }, @@ -44,7 +44,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.4" + "rrweb-snapshot": "^2.0.0-alpha.5" }, "browserslist": [ "supports es6-class" diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md new file mode 100644 index 0000000000..b00e2b7d4b --- /dev/null +++ b/packages/web-extension/CHANGELOG.md @@ -0,0 +1,11 @@ +# @rrweb/web-extension + +## 2.0.0-alpha.5 + +### Patch Changes + +- [#1044](https://github.com/rrweb-io/rrweb/pull/1044) [`282c8fa`](https://github.com/rrweb-io/rrweb/commit/282c8fa415318e56e8b63fa30ce3f173813b39c3) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Add rrweb browser extension + +- Updated dependencies [[`1385f7a`](https://github.com/rrweb-io/rrweb/commit/1385f7acc0052f83be1458a7b00e18c026ee393f), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`227d43a`](https://github.com/rrweb-io/rrweb/commit/227d43abb93d57cadc70c760b28c46911bf7d8ff), [`3cc4323`](https://github.com/rrweb-io/rrweb/commit/3cc4323094065a12f8b65afecd45061d604e245f), [`502d15d`](https://github.com/rrweb-io/rrweb/commit/502d15df9f7f43b3408ccfbb3f14c4bb007883c4), [`8d209a6`](https://github.com/rrweb-io/rrweb/commit/8d209a62f31c4c80e3e5bc36e47d7282ee854ac7)]: + - rrweb@2.0.0-alpha.5 + - rrweb-player@2.0.0-alpha.5 diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 0722339880..406f40bc63 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0-alpha.4", + "version": "2.0.0-alpha.5", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -16,7 +16,7 @@ "prepublish": "npm run pack:chrome && npm run pack:firefox" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.4", + "@rrweb/types": "^2.0.0-alpha.5", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^2.1.0", @@ -41,7 +41,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.4", - "rrweb-player": "^1.0.0-alpha.4" + "rrweb": "^2.0.0-alpha.5", + "rrweb-player": "^2.0.0-alpha.5" } } From f6f07e953376634a4caf28ff8cbfed5a017c4347 Mon Sep 17 00:00:00 2001 From: Prateek Karnal Date: Wed, 15 Feb 2023 07:42:29 -0800 Subject: [PATCH 137/201] Fix relative path detection in stylesheet URLs (#1130) * Fix relative path detection in stylesheet URLs * Add a check for www. * Update packages/rrweb-snapshot/src/snapshot.ts * Update packages/rrweb-snapshot/src/snapshot.ts * Create large-ants-prove.md * Update large-ants-prove.md --------- Co-authored-by: Justin Halsall Co-authored-by: Yun Feng --- .changeset/large-ants-prove.md | 6 ++++++ packages/rrweb-snapshot/src/snapshot.ts | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/large-ants-prove.md diff --git a/.changeset/large-ants-prove.md b/.changeset/large-ants-prove.md new file mode 100644 index 0000000000..9c7681beb3 --- /dev/null +++ b/.changeset/large-ants-prove.md @@ -0,0 +1,6 @@ +--- +'rrweb-snapshot': patch +'rrweb': patch +--- + +Fix: Make relative path detection in stylesheet URLs to detect more types of URL protocols when inlining stylesheets. diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index e8078388a7..5d348a2108 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -71,7 +71,8 @@ let canvasService: HTMLCanvasElement | null; let canvasCtx: CanvasRenderingContext2D | null; const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm; -const RELATIVE_PATH = /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/|#).*/; +const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i; +const URL_WWW_MATCH = /^www\..*/i; const DATA_URI = /^(data:)([^,]*),(.*)/i; export function absoluteToStylesheet( cssText: string | null, @@ -92,7 +93,7 @@ export function absoluteToStylesheet( if (!filePath) { return origin; } - if (!RELATIVE_PATH.test(filePath)) { + if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) { return `url(${maybeQuote}${filePath}${maybeQuote})`; } if (DATA_URI.test(filePath)) { From aaabdbdff5df2abd1a294c40ed89e74bf8b2ec7c Mon Sep 17 00:00:00 2001 From: Ben White Date: Thu, 16 Feb 2023 10:11:31 +0100 Subject: [PATCH 138/201] fix: Recursive logging bug with console recording (#1136) * fix: Recursive logging bug with console recording * Create violet-melons-itch.md --- .changeset/violet-melons-itch.md | 5 + .../rrweb/src/plugins/console/record/index.ts | 9 + .../__snapshots__/integration.test.ts.snap | 180 ++++++++++++++++++ packages/rrweb/test/integration.test.ts | 47 +++++ 4 files changed, 241 insertions(+) create mode 100644 .changeset/violet-melons-itch.md diff --git a/.changeset/violet-melons-itch.md b/.changeset/violet-melons-itch.md new file mode 100644 index 0000000000..af7f60957f --- /dev/null +++ b/.changeset/violet-melons-itch.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +fix: Recursive logging bug with console recording diff --git a/packages/rrweb/src/plugins/console/record/index.ts b/packages/rrweb/src/plugins/console/record/index.ts index 3413cc0727..a3bd4d3843 100644 --- a/packages/rrweb/src/plugins/console/record/index.ts +++ b/packages/rrweb/src/plugins/console/record/index.ts @@ -111,6 +111,7 @@ function initLogObserver( logger = loggerType; } let logCount = 0; + let inStack = false; const cancelHandlers: listenerHandler[] = []; // add listener to thrown errors if (logOptions.level.includes('error')) { @@ -188,6 +189,12 @@ function initLogObserver( (original: (...args: Array) => void) => { return (...args: Array) => { original.apply(this, args); + if (inStack) { + // If we are already in a stack this means something from the following code is calling a console method + // likely a proxy method called from stringify. We don't want to log this as it will cause an infinite loop + return; + } + inStack = true; try { const trace = ErrorStackParser.parse(new Error()) .map((stackFrame: StackFrame) => stackFrame.toString()) @@ -214,6 +221,8 @@ function initLogObserver( } } catch (error) { original('rrweb logger error:', error, ...args); + } finally { + inStack = false; } }; }, diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index ea037dff38..011e3523f3 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -4723,6 +4723,186 @@ exports[`record integration tests mutations should work when blocked class is un ]" `; +exports[`record integration tests should handle recursive console messages 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Log record\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 20 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 6, + \\"data\\": { + \\"plugin\\": \\"rrweb/console@1\\", + \\"payload\\": { + \\"level\\": \\"log\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:20:21\\" + ], + \\"payload\\": [ + \\"\\\\\\"Proxied object:\\\\\\"\\", + \\"\\\\\\"[object Object]\\\\\\"\\" + ] + } + } + } +]" +`; + exports[`record integration tests should mask texts 1`] = ` "[ { diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index fed6aae61a..57fbd072f1 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -542,6 +542,53 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should handle recursive console messages', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml('log.html', { + plugins: + '[rrwebConsoleRecord.getRecordConsolePlugin()]' as unknown as RecordPlugin[], + }), + ); + + await page.evaluate(() => { + // Some frameworks like Vue.js use proxies to implement reactivity. + // This can cause infinite loops when logging objects. + let recursiveTarget = { foo: 'bar', proxied: 'i-am', proxy: null }; + let count = 0; + + const handler = { + get(target: any, prop: any, ...args: any[]) { + if (prop === 'proxied') { + if (count > 9) { + return; + } + count++; // We don't want out test to get into an infinite loop... + console.warn( + 'proxied was accessed so triggering a console.warn', + target, + ); + } + return Reflect.get(target, prop, ...args); + }, + }; + + const proxy = new Proxy(recursiveTarget, handler); + recursiveTarget.proxy = proxy; + + console.log('Proxied object:', proxy); + }); + + await waitForRAF(page); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + // The snapshots should containe 1 console log, not multiple. + assertSnapshot(snapshots); + }); + it('should nest record iframe', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto(`${serverURL}/html`); From 3416c3a769e2bd2ddfbb88f5c4ff139871c567be Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 17 Feb 2023 18:50:58 +1100 Subject: [PATCH 139/201] fix: isCheckout is not included in fullsnapshot event (#1141) * fix: isCheckout is not included in fullsnapshot event * add change log --- .changeset/stupid-ghosts-help.md | 5 +++++ packages/rrweb/src/record/index.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/stupid-ghosts-help.md diff --git a/.changeset/stupid-ghosts-help.md b/.changeset/stupid-ghosts-help.md new file mode 100644 index 0000000000..efc09cd873 --- /dev/null +++ b/.changeset/stupid-ghosts-help.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Fix: isCheckout is missed in all fullsnapshot events diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index c2e7a8855a..28271b9b5f 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -398,6 +398,7 @@ function record( initialOffset: getWindowScroll(window), }, }), + isCheckout, ); mutationBuffers.forEach((buf) => buf.unlock()); // generate & emit any mutations that happened during snapshotting, as can now apply against the newly built mirror From be54981358c958fe2cb0de24b9c25e149c9171cc Mon Sep 17 00:00:00 2001 From: Neil Han Date: Sat, 18 Feb 2023 23:57:47 -0800 Subject: [PATCH 140/201] chore: web-extension vite.config.ts needs these changes to work for node 14 (#1146) --- packages/web-extension/vite.config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/web-extension/vite.config.ts b/packages/web-extension/vite.config.ts index d676e0e608..95809cb727 100644 --- a/packages/web-extension/vite.config.ts +++ b/packages/web-extension/vite.config.ts @@ -21,9 +21,10 @@ function useSpecialFormat( (config.build?.lib as LibraryOptions)?.entry, ); if (shouldUse) { - config.build ??= {}; + config.build = config.build ?? {}; // @ts-expect-error: lib needs to be an object, forcing it. - config.build.lib ||= {}; + config.build.lib = + typeof config.build.lib == 'object' ? config.build.lib : {}; // @ts-expect-error: lib is an object config.build.lib.formats = [format]; } From c28ef5f658abb93086504581409cf7a376db48dc Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 22 Feb 2023 05:45:29 +0000 Subject: [PATCH 141/201] =?UTF-8?q?Replay=20fix=20for=20Firefox=20?= =?UTF-8?q?=E2=80=94=20add=20=20and=20=20separately=20(#1133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Firefox fix: Allow the and to be added in two stages so that (presumably) stylesheet rules are ready to be applied when the body appears The css which triggered the bug was simply { margin-left: 220px; transition: margin-left .448s; } * Add a test case which can only be appreciated if you record against this file://, save the events to a html file, and then open the file in Firefox (without this PR applied) * Apply formatting changes * Ensure we don't apply this branch when using rrdom, where it is not necessary * Apply formatting changes * Rewrite insertion in order to be compatible with rrdom Also easier to understand * Delete transition.html * Create grumpy-ways-own.md --------- Co-authored-by: Yun Feng --- .changeset/grumpy-ways-own.md | 7 +++++++ packages/rrweb-snapshot/src/rebuild.ts | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 .changeset/grumpy-ways-own.md diff --git a/.changeset/grumpy-ways-own.md b/.changeset/grumpy-ways-own.md new file mode 100644 index 0000000000..f34169219a --- /dev/null +++ b/.changeset/grumpy-ways-own.md @@ -0,0 +1,7 @@ +--- +'rrweb-snapshot': patch +--- + +Fix: CSS transitions are incorrectly being applied upon rebuild in Firefox. Presumably FF doesn't finished parsing the styles in time, and applies e.g. a default margin:0 to elements which have a non-zero margin set in CSS, along with a transition on them. + +Related bug report to Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1816672​ diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 1582104a45..ee01fcde97 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -438,6 +438,28 @@ export function buildNodeWithSN( if (childN.isShadow && isElement(node) && node.shadowRoot) { node.shadowRoot.appendChild(childNode); + } else if ( + n.type === NodeType.Document && + childN.type == NodeType.Element + ) { + const htmlElement = childNode as HTMLElement; + let body: HTMLBodyElement | null = null; + htmlElement.childNodes.forEach((child) => { + if (child.nodeName === 'BODY') body = child as HTMLBodyElement; + }); + if (body) { + // this branch solves a problem in Firefox where css transitions are incorrectly + // being applied upon rebuild. Presumably FF doesn't finished parsing the styles + // in time, and applies e.g. a default margin:0 to elements which have a non-zero + // margin set in CSS, along with a transition on them + htmlElement.removeChild(body); + // append and + + +
+
+ + + `, + ); + + describe('CSSStyleSheet.prototype', () => { + it('triggers for errors from insertRule', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleSheet.prototype.insertRule = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + // @ts-ignore + document.styleSheets[0].insertRule('body { background: blue; }'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from deleteRule', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleSheet.prototype.deleteRule = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet delete + setTimeout(() => { + document.styleSheets[0].deleteRule(0); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from replace', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleSheet.prototype.replace = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + // @ts-ignore + document.styleSheets[0].replace('body { background: blue; }'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from replaceSync', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleSheet.prototype.replaceSync = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + // @ts-ignore + document.styleSheets[0].replaceSync('body { background: blue; }'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from CSSGroupingRule.insertRule', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSGroupingRule.prototype.insertRule = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + document.styleSheets[0].insertRule('@media {}'); + const atMediaRule = document.styleSheets[0] + .cssRules[0] as CSSMediaRule; + + const ruleIdx0 = atMediaRule.insertRule( + 'body { background: #000; }', + 0, + ); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from CSSGroupingRule.deleteRule', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSGroupingRule.prototype.deleteRule = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet delete + setTimeout(() => { + document.styleSheets[0].insertRule('@media {}'); + const atMediaRule = document.styleSheets[0] + .cssRules[0] as CSSMediaRule; + + const ruleIdx0 = atMediaRule.deleteRule(0); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from CSSStyleDeclaration.setProperty', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleDeclaration.prototype.setProperty = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + ( + document.styleSheets[0].cssRules[0] as unknown as { + style: CSSStyleDeclaration; + } + ).style.setProperty('background', 'blue'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + + it('triggers for errors from CSSStyleDeclaration.removeProperty', async () => { + await ctx.page.evaluate(() => { + // @ts-ignore rewrite this to something buggy + window.CSSStyleDeclaration.prototype.removeProperty = function () { + // @ts-ignore + window.doSomethingWrong(); + }; + }); + + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy style sheet insert + setTimeout(() => { + ( + document.styleSheets[0].cssRules[0] as unknown as { + style: CSSStyleDeclaration; + } + ).style.removeProperty('background'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual( + 'TypeError: window.doSomethingWrong is not a function', + ); + }); + }); + + it('triggers for errors from mutation observer', async () => { + await ctx.page.evaluate(() => { + const { record } = (window as unknown as IWindow).rrweb; + record({ + errorHandler: (error) => { + document.getElementById('out')!.innerText = `${error}`; + }, + emit: (window as unknown as IWindow).emit, + }); + + // Trigger buggy mutation observer + setTimeout(() => { + const el = document.getElementById('in')!; + + // @ts-ignore we want to trigger an error in the mutation observer, which uses this + el.getAttribute = undefined; + + el.setAttribute('data-attr', 'new'); + }, 50); + }); + + await ctx.page.waitForTimeout(100); + + const element = await ctx.page.$('#out'); + const text = await element!.evaluate((el) => el.textContent); + + expect(text).toEqual('TypeError: m.target.getAttribute is not a function'); + }); +}); + +describe('errorHandler unit', function () { + afterEach(function () { + unregisterErrorHandler(); + }); + + it('does not swallow if no errorHandler set', () => { + unregisterErrorHandler(); + + const wrapped = callbackWrapper(() => { + throw new Error('test'); + }); + + expect(() => wrapped()).toThrowError('test'); + }); + + it('does not swallow if errorHandler returns void', () => { + registerErrorHandler(() => { + // do nothing + }); + + const wrapped = callbackWrapper(() => { + throw new Error('test'); + }); + + expect(() => wrapped()).toThrowError('test'); + }); + + it('does not swallow if errorHandler returns false', () => { + registerErrorHandler(() => { + return false; + }); + + const wrapped = callbackWrapper(() => { + throw new Error('test'); + }); + + expect(() => wrapped()).toThrowError('test'); + }); + + it('swallows if errorHandler returns true', () => { + registerErrorHandler(() => { + return true; + }); + + const wrapped = callbackWrapper(() => { + throw new Error('test'); + }); + + expect(() => wrapped()).not.toThrowError('test'); + }); +}); From 267e990dc0e45a5acaaa3ee89db7ae9171520d54 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 24 Mar 2023 17:55:22 +1100 Subject: [PATCH 158/201] Fix: processed-node-manager is created even in the environment that doesn't need a recorder (#1186) * Fix: processed-node-manager is created even in the environment that doesn't need a recorder * apply Justin's suggestion End the RAF loop when the recorder stops --- .changeset/fresh-spoons-drive.md | 5 +++++ guide.zh_CN.md | 1 + packages/rrweb/src/record/index.ts | 10 +++++----- packages/rrweb/src/record/observer.ts | 2 -- packages/rrweb/src/record/processed-node-manager.ts | 9 ++++++++- packages/rrweb/src/utils.ts | 3 ++- 6 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 .changeset/fresh-spoons-drive.md diff --git a/.changeset/fresh-spoons-drive.md b/.changeset/fresh-spoons-drive.md new file mode 100644 index 0000000000..b99410373c --- /dev/null +++ b/.changeset/fresh-spoons-drive.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Fix: processed-node-manager is created even in the environment that doesn't need a recorder diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 8e6f2c5a2b..28c846c0ac 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -159,6 +159,7 @@ setInterval(save, 10 * 1000); | collectFonts | false | 是否记录页面中的字体文件 | | userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) | | plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | +| errorHandler | - | 一个可以定制化处理错误的毁掉函数,它的参数是错误对象。如果 rrweb recorder 内部的某些内容抛出错误,则会调用该回调。 | #### 隐私 diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 54d8eafa2a..c0e69a025f 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -4,11 +4,7 @@ import { SlimDOMOptions, createMirror, } from 'rrweb-snapshot'; -import { - initObservers, - mutationBuffers, - processedNodeManager, -} from './observer'; +import { initObservers, mutationBuffers } from './observer'; import { on, getWindowWidth, @@ -36,6 +32,7 @@ import { IframeManager } from './iframe-manager'; import { ShadowDomManager } from './shadow-dom-manager'; import { CanvasManager } from './observers/canvas/canvas-manager'; import { StylesheetManager } from './stylesheet-manager'; +import ProcessedNodeManager from './processed-node-manager'; import { callbackWrapper, registerErrorHandler, @@ -306,6 +303,8 @@ function record( }); } + const processedNodeManager = new ProcessedNodeManager(); + canvasManager = new CanvasManager({ recordCanvas, mutationCb: wrappedCanvasMutationEmit, @@ -616,6 +615,7 @@ function record( } return () => { handlers.forEach((h) => h()); + processedNodeManager.destroy(); recording = false; unregisterErrorHandler(); }; diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index d02c963983..d2852b970a 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -41,7 +41,6 @@ import { selectionCallback, } from '@rrweb/types'; import MutationBuffer from './mutation'; -import ProcessedNodeManager from './processed-node-manager'; import { callbackWrapper } from './error-handler'; type WindowWithStoredMutationObserver = IWindow & { @@ -54,7 +53,6 @@ type WindowWithAngularZone = IWindow & { }; export const mutationBuffers: MutationBuffer[] = []; -export const processedNodeManager = new ProcessedNodeManager(); // Event.path is non-standard and used in some older browsers type NonStandardEvent = Omit & { diff --git a/packages/rrweb/src/record/processed-node-manager.ts b/packages/rrweb/src/record/processed-node-manager.ts index 60b939bb12..b5d6c4b679 100644 --- a/packages/rrweb/src/record/processed-node-manager.ts +++ b/packages/rrweb/src/record/processed-node-manager.ts @@ -5,6 +5,8 @@ import type MutationBuffer from './mutation'; */ export default class ProcessedNodeManager { private nodeMap: WeakMap> = new WeakMap(); + // Whether to continue RAF loop. + private loop = true; constructor() { this.periodicallyClear(); @@ -13,7 +15,7 @@ export default class ProcessedNodeManager { private periodicallyClear() { requestAnimationFrame(() => { this.clear(); - this.periodicallyClear(); + if (this.loop) this.periodicallyClear(); }); } @@ -31,4 +33,9 @@ export default class ProcessedNodeManager { private clear() { this.nodeMap = new WeakMap(); } + + public destroy() { + // Stop the RAF loop. + this.loop = false; + } } diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 2ed5ba52d3..1626e3734c 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -574,6 +574,7 @@ export function getInputType(element: HTMLElement): Lowercase | null { return element.hasAttribute('data-rr-is-password') ? 'password' : element.hasAttribute('type') - ? (element.getAttribute('type')!.toLowerCase() as Lowercase) + ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion + (element.getAttribute('type')!.toLowerCase() as Lowercase) : null; } From 0138ab83663bf670cf710573663b4dd0f08a8016 Mon Sep 17 00:00:00 2001 From: yz-yu Date: Sat, 25 Mar 2023 14:51:50 +0800 Subject: [PATCH 159/201] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 48c791019c..1c37c7da24 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -2,7 +2,7 @@ github: [Yuyz0112] patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username +open_collective: rrweb ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry From 1e6f71b3cddcfafe78b9e40edfbd75e485702e4e Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 28 Mar 2023 18:59:40 +0200 Subject: [PATCH 160/201] Fix: Trigger mouse movement & hover with mouse up/down in sync mode (#1191) * Trigger mouse movement & hover with mouse up/down in sync mode * Trigger touchActive and mouseDown on flush --- .changeset/sixty-impalas-laugh.md | 5 + .changeset/violet-zebras-cry.md | 5 + packages/rrweb/src/replay/index.ts | 33 +++-- packages/rrweb/test/events/hover.ts | 113 ++++++++++++++++++ ...uld-trigger-hover-on-mouse-down-1-snap.png | Bin 0 -> 11774 bytes packages/rrweb/test/replay/hover.test.ts | 71 +++++++++++ 6 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 .changeset/sixty-impalas-laugh.md create mode 100644 .changeset/violet-zebras-cry.md create mode 100644 packages/rrweb/test/events/hover.ts create mode 100644 packages/rrweb/test/replay/__image_snapshots__/hover-test-ts-replayer-hover-should-trigger-hover-on-mouse-down-1-snap.png create mode 100644 packages/rrweb/test/replay/hover.test.ts diff --git a/.changeset/sixty-impalas-laugh.md b/.changeset/sixty-impalas-laugh.md new file mode 100644 index 0000000000..95d8d26362 --- /dev/null +++ b/.changeset/sixty-impalas-laugh.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Only apply touch-active styling on flush diff --git a/.changeset/violet-zebras-cry.md b/.changeset/violet-zebras-cry.md new file mode 100644 index 0000000000..5f4c5244c8 --- /dev/null +++ b/.changeset/violet-zebras-cry.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Trigger mouse movement and hover with mouse up and mouse down events when replayer.pause(...) is called. diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 91ee9d5f17..1bc9c6a6a1 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -148,6 +148,7 @@ export class Replayer { private mousePos: mouseMovePos | null = null; private touchActive: boolean | null = null; + private lastMouseDownEvent: [Node, Event] | null = null; // Keep the rootNode of the last hovered element. So when hovering a new element, we can remove the last hovered element's :hover style. private lastHoveredRootNode: Document | ShadowRoot; @@ -299,6 +300,20 @@ export class Replayer { ); this.mousePos = null; } + + if (this.touchActive === true) { + this.mouse.classList.add('touch-active'); + } else if (this.touchActive === false) { + this.mouse.classList.remove('touch-active'); + } + this.touchActive = null; + + if (this.lastMouseDownEvent) { + const [target, event] = this.lastMouseDownEvent; + target.dispatchEvent(event); + } + this.lastMouseDownEvent = null; + if (this.lastSelectionData) { this.applySelection(this.lastSelectionData); this.lastSelectionData = null; @@ -614,12 +629,6 @@ export class Replayer { const castFn = this.getCastFn(event, true); castFn(); } - if (this.touchActive === true) { - this.mouse.classList.add('touch-active'); - } else if (this.touchActive === false) { - this.mouse.classList.remove('touch-active'); - } - this.touchActive = null; }; private getCastFn = (event: eventWithTime, isSync = false) => { @@ -1108,7 +1117,7 @@ export class Replayer { /** * Same as the situation of missing input target. */ - if (d.id === -1 || isSync) { + if (d.id === -1) { break; } const event = new Event(MouseInteractions[d.type].toLowerCase()); @@ -1137,12 +1146,19 @@ export class Replayer { case MouseInteractions.Click: case MouseInteractions.TouchStart: case MouseInteractions.TouchEnd: + case MouseInteractions.MouseDown: + case MouseInteractions.MouseUp: if (isSync) { if (d.type === MouseInteractions.TouchStart) { this.touchActive = true; } else if (d.type === MouseInteractions.TouchEnd) { this.touchActive = false; } + if (d.type === MouseInteractions.MouseDown) { + this.lastMouseDownEvent = [target, event]; + } else if (d.type === MouseInteractions.MouseUp) { + this.lastMouseDownEvent = null; + } this.mousePos = { x: d.x, y: d.y, @@ -1172,6 +1188,9 @@ export class Replayer { this.mouse.classList.add('touch-active'); } else if (d.type === MouseInteractions.TouchEnd) { this.mouse.classList.remove('touch-active'); + } else { + // for MouseDown & MouseUp also invoke default behavior + target.dispatchEvent(event); } } break; diff --git a/packages/rrweb/test/events/hover.ts b/packages/rrweb/test/events/hover.ts new file mode 100644 index 0000000000..73b2a4ea55 --- /dev/null +++ b/packages/rrweb/test/events/hover.ts @@ -0,0 +1,113 @@ +import { IncrementalSource, MouseInteractions } from '@rrweb/types'; +import type { eventWithTime } from '../../../types/src'; + +const events: eventWithTime[] = [ + { + type: 4, + data: { + href: '', + width: 1600, + height: 900, + }, + timestamp: 0, + }, + { + type: 2, + data: { + node: { + type: 0, + childNodes: [ + { type: 1, name: 'html', publicId: '', systemId: '', id: 2 }, + { + type: 2, + tagName: 'html', + attributes: { lang: 'en' }, + childNodes: [ + { + type: 2, + tagName: 'head', + attributes: {}, + childNodes: [ + { + id: 101, + type: 2, + tagName: 'style', + attributes: {}, + childNodes: [ + { + id: 102, + type: 3, + isStyle: true, + textContent: 'div:hover { background-color: gold; }', + }, + ], + }, + ], + id: 4, + }, + { type: 3, textContent: '\n ', id: 13 }, + { + type: 2, + tagName: 'body', + attributes: {}, + childNodes: [ + { type: 3, textContent: '\n ', id: 15 }, + { + type: 2, + tagName: 'div', + attributes: { + style: + 'border: 1px solid #000000; width: 100px; height: 100px;', + }, + childNodes: [{ type: 3, textContent: '\n ', id: 17 }], + id: 16, + }, + ], + id: 14, + }, + ], + id: 3, + }, + ], + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: 10, + }, + { + type: 3, + data: { + source: IncrementalSource.MouseInteraction, + type: MouseInteractions.MouseDown, + id: 16, + x: 30, + y: 30, + }, + timestamp: 100, + }, + { + type: 3, + data: { + source: IncrementalSource.MouseInteraction, + type: MouseInteractions.MouseUp, + id: 16, + x: 30, + y: 30, + }, + timestamp: 150, + }, + { + type: 3, + data: { + source: IncrementalSource.MouseInteraction, + type: MouseInteractions.Click, + id: 16, + x: 30, + y: 30, + }, + timestamp: 155, + }, +]; + +export default events; diff --git a/packages/rrweb/test/replay/__image_snapshots__/hover-test-ts-replayer-hover-should-trigger-hover-on-mouse-down-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/hover-test-ts-replayer-hover-should-trigger-hover-on-mouse-down-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..4142d17a7fdf077bec94553adcd307f87ef00652 GIT binary patch literal 11774 zcmeHNdrVtZ7(azFIvp?PWXfa1IZ-#pNEKLIscuZfDHwGR9-}pL?X)nqU>K}V90U-j zPJ--?(z=iZr>>Mu#yTD?ZZdT(SF;VUqiY3MHXgmjj+R0n-u7NhOvo}ajmfqm0RTWi>dVP*0N^hIfSgEKfZn-zxoac(M=E$DWdo4) zgx>;SIY>=@@vS29m@e+4NMnX-;$lre{Btc0nfq3TDZYAaM3Ovyg1oC)8zDZ%5eJ6U zeEDhUg3twzTv_;bT>PRyPWi*emr9@Ba&BoO0a9*UUQ~#|ux*Y%J8m zHk}i(Wu@u)NQZ4q*nx1F9P~o1@Lc_tq?Qp9)&|=HNlUTj@(sXaE_zi2i}3Ep$xQDZ zAfn76mN@mLU#!ycN17TFmp_Cmu?!TzCl4$V9pCGN4cy<_+uO^^%gejE6Rg&(NC)}c zTgPi9zlUQZKW{SW+P;n?F;9r+tA7qND-K@L-c*(NUebQJQmfTwPEs%}s=$m)Ac20J zZrx?-ZW>k9ZlmF^D6h^Y1z*_{f^{vGSr3*J_+0NsnsqXO>1>dILd6F+Len;?!lmT&VpJ<@!eA;h6 z;&N6=O$W)5I*63(I=QSp136ru#rO-4<*iz%8Vzd{9-nF(x9X^;JNLa;`Bx_&?M&!X zo-L(GGQN8PA1&f}uZ^ASTRgx;6oLh^u*rA{4u$ z%4r=U6%|;_f0uEbbxrEoIWxbgJKPzQv#Pk~yXsx#<$*e>e_cwX!qk(_7EE=k>Fx2B zGCgEJSU^<@O`$d)T-naaex>vj{%WtyolK#Ekj5W@QM)aNtCh=!zh6}q>=?9MlHlyYv}L{vBpKQMeAGc1 z`o!n<>$wL^I(mFvT{)X&{Q1~S!EJMQcKcqofamO8Z>vc`qFuP&nX5nfn$L^}JJ#x_ zyVYfzQO``>;8rMc_z2FNN)XX2hoq3*aRPErt!$+;gk0lf0(*^sKWv;0?Q7O#)By2F z$FwcTZJNGD9fo@xH(EoZ%iN>N{e|&(G;~auY^Y)|#Aau4Y!0NDil(A&OCG!wTcN@g zy1wOH2ZC_V^M;lnS@Cs&#Wqs2>2|%{;MAkh%@OX}$A5O#I(egy`Nn4IHbw^7Fh125 z!D9$Fk#Q!qw$he}VCjx_+l&U`J?rXO{df}vPn|I~_Za}M{?0U^ckOoQta6HDx{lf> zkQasFKK&vW_A{iE4FshagszDpCr(o@bTw|JsjB4>C2)Pc!m8Vnjw~-9R3K7@NEsq!h?JRg$B`fh zf*k$_#qYLcHuS|E=k}+`%Tqy#Af{ z&OhNd!f$sz8zK;L{~_c;9?0R)n(-}o(Z)4*cXs-ptYppxsJKXUAvS%0y+8QP^K6sA z?>~uOp`i0gv|!*HPb~l*5@OErI^rIV-wBBRlMp3f2>>yrdr@$=^)EyM5(#)ODTCxk zYSg3WWQ=_LpxK}r_cN>8S;op3oQ}e`a}g(ke+2`v#h+Nlpq?QnO)os|1$*Aqg*}TN^d|$^9f5p9xf@vvqGrf>oG2 nSd7;r1LlU=GT&Dz%=ryQ@vWgIl|AK5^E{TiDJ@yHf%(Co`@Dcl literal 0 HcmV?d00001 diff --git a/packages/rrweb/test/replay/hover.test.ts b/packages/rrweb/test/replay/hover.test.ts new file mode 100644 index 0000000000..d4cdec897e --- /dev/null +++ b/packages/rrweb/test/replay/hover.test.ts @@ -0,0 +1,71 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { launchPuppeteer, waitForRAF } from '../utils'; +import { toMatchImageSnapshot } from 'jest-image-snapshot'; +import type * as puppeteer from 'puppeteer'; +import events from '../events/hover'; + +interface ISuite { + code: string; + styles: string; + browser: puppeteer.Browser; + page: puppeteer.Page; +} + +expect.extend({ toMatchImageSnapshot }); + +describe('replayer', function () { + jest.setTimeout(10_000); + + let code: ISuite['code']; + let styles: ISuite['styles']; + let browser: ISuite['browser']; + let page: ISuite['page']; + + beforeAll(async () => { + browser = await launchPuppeteer({ devtools: true }); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.js'); + const stylePath = path.resolve( + __dirname, + '../../src/replay/styles/style.css', + ); + code = fs.readFileSync(bundlePath, 'utf8'); + styles = fs.readFileSync(stylePath, 'utf8'); + }); + + beforeEach(async () => { + page = await browser.newPage(); + await page.goto('about:blank'); + await page.addStyleTag({ + content: styles, + }); + await page.evaluate(code); + await page.evaluate(`let events = ${JSON.stringify(events)}`); + + page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + }); + + afterEach(async () => { + await page.close(); + }); + + afterAll(async () => { + await browser.close(); + }); + + describe('hover', () => { + it('should trigger hover on mouseDown', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events); + replayer.pause(110); // mouseDown event is at 100 + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + }); +}); From f1f5865dcf19db5637bbb12b220eb2aa0c0219ad Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Wed, 29 Mar 2023 04:02:22 +1100 Subject: [PATCH 161/201] move rrvideo to monorepo (#1181) * first commit * rrvideo v0.1.0 First version of rrvideo. 1. Use as a Node.JS lib. 2. Use as a CLI. Features are implemented via puppeteer, ffmpeg and rrweb-player. * add readme * update publish script * add node env in cli file and change package.json bin to same like README (#4) Co-authored-by: Xu Yinjie * release 0.2.0 * fix #6 avoid assign undefined to config * Fix: Solve the inconsistency between rrvideo and the real recorded page rendering when rendering the page with a headless browser (https://github.com/rrweb-io/rrvideo/pull/26) Author: xujiujiu <906784584@qq.com> --------- Co-authored-by: xujiujiu <906784584@qq.com> * refactor rrvideo 1. refactor code 2. change monorepo config 3. remove separate TS dependencies * add changeset * fix: eslint errors --------- Co-authored-by: Yanzhen Yu Co-authored-by: xyj <593500664@qq.com> Co-authored-by: Xu Yinjie Co-authored-by: xujiujiu <906784584@qq.com> --- .changeset/config.json | 3 +- .changeset/tricky-panthers-guess.md | 5 + .github/ISSUE_TEMPLATE/bug_report.yml | 2 + .github/ISSUE_TEMPLATE/feature_request.yml | 2 + .vscode/rrweb-monorepo.code-workspace | 1 + package.json | 2 - packages/rrdom-nodejs/package.json | 3 +- packages/rrdom/package.json | 3 +- packages/rrvideo/README.md | 37 + packages/rrvideo/README.zh_CN.md | 35 + packages/rrvideo/package.json | 28 + packages/rrvideo/rrvideo.config.example.json | 10 + packages/rrvideo/src/cli.ts | 39 + packages/rrvideo/src/index.ts | 217 ++ packages/rrvideo/tsconfig.json | 27 + packages/rrweb-player/package.json | 3 +- packages/rrweb-player/src/Controller.svelte | 2 +- packages/rrweb-player/src/Player.svelte | 2 +- packages/rrweb-snapshot/package.json | 3 +- packages/rrweb/package.json | 3 +- packages/types/package.json | 1 - packages/web-extension/package.json | 1 - packages/web-extension/tsconfig.json | 17 +- tsconfig.json | 6 + yarn.lock | 2678 ++---------------- 25 files changed, 597 insertions(+), 2533 deletions(-) create mode 100644 .changeset/tricky-panthers-guess.md create mode 100644 packages/rrvideo/README.md create mode 100644 packages/rrvideo/README.zh_CN.md create mode 100644 packages/rrvideo/package.json create mode 100644 packages/rrvideo/rrvideo.config.example.json create mode 100644 packages/rrvideo/src/cli.ts create mode 100644 packages/rrvideo/src/index.ts create mode 100644 packages/rrvideo/tsconfig.json diff --git a/.changeset/config.json b/.changeset/config.json index 41a818e7ba..962e9799b0 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", + "rrvideo" ] ], "linked": [], diff --git a/.changeset/tricky-panthers-guess.md b/.changeset/tricky-panthers-guess.md new file mode 100644 index 0000000000..3dfadbaed3 --- /dev/null +++ b/.changeset/tricky-panthers-guess.md @@ -0,0 +1,5 @@ +--- +'rrvideo': patch +--- + +Refactor: Move rrvideo to rrweb's monorepo diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1fff641682..cb5885c385 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -19,6 +19,8 @@ body: - rrweb-snapshot - rrdom - rrweb-player + - web-extension + - rrvideo - Other (specify below) validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index df2a0d7f2a..0bbb65aa43 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -19,6 +19,8 @@ body: - rrweb-snapshot - rrdom - rrweb-player + - web-extension + - rrvideo - Other (specify below) validations: required: true diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index 43fd69fb85..ee31ea35ee 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -28,6 +28,7 @@ "name": "web-extension (package)", "path": "../packages/web-extension" }, + { "name": "rrvideo (package)", "path": "../packages/rrvideo" }, { "name": "@rrweb/types", "path": "../packages/types" } ], "settings": { diff --git a/package.json b/package.json index 6246830b06..60096855a9 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "eslint-plugin-compat": "^4.0.2", "eslint-plugin-jest": "^27.1.3", "eslint-plugin-tsdoc": "^0.2.16", - "lerna": "^4.0.0", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", "prettier": "2.8.4", @@ -37,7 +36,6 @@ "typescript": "^4.7.3" }, "scripts": { - "lerna": "lerna", "build:all": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references' 'yarn turbo run prepublish'", "test": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references --check' 'yarn turbo run test'", "test:watch": "yarn turbo run test:watch", diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index ee1a4d83cf..f1e3dfa873 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -42,8 +42,7 @@ "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", - "ts-jest": "^27.1.3", - "typescript": "^4.7.3" + "ts-jest": "^27.1.3" }, "dependencies": { "cssom": "^0.5.0", diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 98d05272e6..e7ea231d6a 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -44,8 +44,7 @@ "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-web-worker-loader": "^1.6.1", - "ts-jest": "^27.1.3", - "typescript": "^4.7.3" + "ts-jest": "^27.1.3" }, "dependencies": { "rrweb-snapshot": "^2.0.0-alpha.6" diff --git a/packages/rrvideo/README.md b/packages/rrvideo/README.md new file mode 100644 index 0000000000..25d988aa0d --- /dev/null +++ b/packages/rrvideo/README.md @@ -0,0 +1,37 @@ +# rrvideo + +[中文文档](./README.zh_CN.md) + +rrvideo is a tool for transforming the session recorded by [rrweb](https://github.com/rrweb-io/rrweb) into a video. + +## Install rrvideo + +1. Install [ffmpeg](https://ffmpeg.org/download.html)。 +2. Install [Node.JS](https://nodejs.org/en/download/)。 +3. Run `npm i -g rrvideo` to install the rrvideo CLI。 + +## Use rrvideo + +### Transform a rrweb session(in JSON format) into a video. + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE +``` + +Running this command will output a `rrvideo-output.mp4` file in the current working directory. + +### Config the output path + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE --output OUTPUT_PATH +``` + +### Config the replay + +You can prepare a rrvideo config file and pass it to CLI. + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_JSON_FILE --config PATH_TO_YOUR_RRVIDEO_CONFIG_FILE +``` + +You can find an example of the rrvideo config file [here](./rrvideo.config.example.json). diff --git a/packages/rrvideo/README.zh_CN.md b/packages/rrvideo/README.zh_CN.md new file mode 100644 index 0000000000..d133e87335 --- /dev/null +++ b/packages/rrvideo/README.zh_CN.md @@ -0,0 +1,35 @@ +# rrvideo + +rrvideo 是用于将 [rrweb](https://github.com/rrweb-io/rrweb) 录制的数据转为视频格式的工具。 + +## 安装 rrvideo + +1. 安装 [ffmpeg](https://ffmpeg.org/download.html)。 +2. 安装 [Node.JS](https://nodejs.org/en/download/)。 +3. 执行 `npm i -g rrvideo` 以安装 rrvideo CLI。 + +## 使用 rrvideo + +### 将一份 rrweb 录制的数据(JSON 格式)转换为视频。 + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE +``` + +运行以上命令会在执行文件夹中生成一个 `rrvideo-output.mp4` 文件。 + +### 指定输出路径 + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE --output OUTPUT_PATH +``` + +### 对回放进行配置 + +通过编写一个 rrvideo 配置文件再传入 rrvideo CLI 的方式可以对回放进行一定的配置。 + +```shell +rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_JSON_FILE --config PATH_TO_YOUR_RRVIDEO_CONFIG_FILE +``` + +rrvideo 配置文件可参考[示例](./rrvideo.config.example.json)。 diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json new file mode 100644 index 0000000000..b1a00b0b61 --- /dev/null +++ b/packages/rrvideo/package.json @@ -0,0 +1,28 @@ +{ + "name": "rrvideo", + "version": "2.0.0-alpha.6", + "description": "transform rrweb session into video", + "main": "build/index.js", + "bin": { + "rrvideo": "build/cli.js" + }, + "files": [ + "build" + ], + "types": "build/index.d.ts", + "scripts": { + "build": "tsc", + "prepublish": "yarn build" + }, + "author": "yanzhen@smartx.com", + "license": "MIT", + "devDependencies": { + "@types/minimist": "^1.2.1", + "@rrweb/types": "^2.0.0-alpha.6" + }, + "dependencies": { + "minimist": "^1.2.5", + "puppeteer": "^19.7.2", + "rrweb-player": "^2.0.0-alpha.6" + } +} diff --git a/packages/rrvideo/rrvideo.config.example.json b/packages/rrvideo/rrvideo.config.example.json new file mode 100644 index 0000000000..d1445e3006 --- /dev/null +++ b/packages/rrvideo/rrvideo.config.example.json @@ -0,0 +1,10 @@ +{ + "width": 1400, + "height": 900, + "speed": 4, + "skipInactive": true, + "mouseTail": { + "strokeStyle": "green", + "lineWidth": 2 + } +} diff --git a/packages/rrvideo/src/cli.ts b/packages/rrvideo/src/cli.ts new file mode 100644 index 0000000000..318e8fa349 --- /dev/null +++ b/packages/rrvideo/src/cli.ts @@ -0,0 +1,39 @@ +#!/usr/bin/env node +import * as fs from 'fs'; +import * as path from 'path'; +import minimist from 'minimist'; +import type { RRwebPlayerOptions } from 'rrweb-player'; +import { transformToVideo } from './index'; + +const argv = minimist(process.argv.slice(2)); + +if (!argv.input) { + throw new Error('please pass --input to your rrweb events file'); +} + +let config = {}; + +if (argv.config) { + const configPathStr = argv.config as string; + const configPath = path.isAbsolute(configPathStr) + ? configPathStr + : path.resolve(process.cwd(), configPathStr); + config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) as Omit< + RRwebPlayerOptions['props'], + 'events' + >; +} + +transformToVideo({ + input: argv.input as string, + output: argv.output as string, + rrwebPlayer: config, +}) + .then((file) => { + console.log(`Successfully transformed into "${file}".`); + }) + .catch((error) => { + console.log('Failed to transform this session.'); + console.error(error); + process.exit(1); + }); diff --git a/packages/rrvideo/src/index.ts b/packages/rrvideo/src/index.ts new file mode 100644 index 0000000000..99471a8c28 --- /dev/null +++ b/packages/rrvideo/src/index.ts @@ -0,0 +1,217 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { spawn } from 'child_process'; +import puppeteer from 'puppeteer'; +import type { Page, Browser } from 'puppeteer'; +import type { eventWithTime } from '@rrweb/types'; +import type { RRwebPlayerOptions } from 'rrweb-player'; + +const rrwebScriptPath = path.resolve( + require.resolve('rrweb-player'), + '../../dist/index.js', +); +const rrwebStylePath = path.resolve(rrwebScriptPath, '../style.css'); +const rrwebRaw = fs.readFileSync(rrwebScriptPath, 'utf-8'); +const rrwebStyle = fs.readFileSync(rrwebStylePath, 'utf-8'); + +type RRvideoConfig = { + input: string; + output?: string; + headless?: boolean; + fps?: number; + cb?: (file: string, error: null | Error) => void; + // start playback delay time + startDelayTime?: number; + rrwebPlayer?: Omit; +}; + +const defaultConfig: Required = { + input: '', + output: 'rrvideo-output.mp4', + headless: true, + fps: 15, + cb: () => { + // + }, + startDelayTime: 1000, + rrwebPlayer: {}, +}; + +function getHtml( + events: Array, + config?: Omit, +): string { + return ` + + + + + + + + +`; +} + +export class RRvideo { + private browser!: Browser; + private page!: Page; + private state: 'idle' | 'recording' | 'closed' = 'idle'; + private config = { + ...defaultConfig, + }; + + constructor(config: RRvideoConfig) { + this.updateConfig(config); + } + + public async transform() { + try { + this.browser = await puppeteer.launch({ + headless: this.config.headless, + }); + this.page = await this.browser.newPage(); + await this.page.goto('about:blank'); + + await this.page.exposeFunction('onReplayFinish', () => { + void this.finishRecording(); + }); + + const eventsPath = path.isAbsolute(this.config.input) + ? this.config.input + : path.resolve(process.cwd(), this.config.input); + const events = JSON.parse( + fs.readFileSync(eventsPath, 'utf-8'), + ) as eventWithTime[]; + + await this.page.setContent(getHtml(events, this.config.rrwebPlayer)); + + setTimeout(() => { + void this.startRecording().then(() => { + return this.page.evaluate('window.replayer.play();'); + }); + }, this.config.startDelayTime); + } catch (error) { + this.config.cb('', error as Error); + } + } + + public updateConfig(config: RRvideoConfig) { + if (!config.input) throw new Error('input is required'); + config.output = config.output || defaultConfig.output; + Object.assign(this.config, defaultConfig, config); + } + + private async startRecording() { + this.state = 'recording'; + let wrapperSelector = '.replayer-wrapper'; + if (this.config.rrwebPlayer.width && this.config.rrwebPlayer.height) { + wrapperSelector = '.rr-player'; + } + const wrapperEl = await this.page.$(wrapperSelector); + + if (!wrapperEl) { + throw new Error('failed to get replayer element'); + } + + // start ffmpeg + const args = [ + // fps + '-framerate', + this.config.fps.toString(), + // input + '-f', + 'image2pipe', + '-i', + '-', + // output + '-y', + this.config.output, + ]; + + const ffmpegProcess = spawn('ffmpeg', args); + ffmpegProcess.stderr.setEncoding('utf-8'); + ffmpegProcess.stderr.on('data', console.log); + + let processError: Error | null = null; + + const timer = setInterval(() => { + if (this.state === 'recording' && !processError) { + void wrapperEl + .screenshot({ + encoding: 'binary', + }) + .then((buffer) => ffmpegProcess.stdin.write(buffer)) + .catch(); + } else { + clearInterval(timer); + if (this.state === 'closed' && !processError) { + ffmpegProcess.stdin.end(); + } + } + }, 1000 / this.config.fps); + + const outputPath = path.isAbsolute(this.config.output) + ? this.config.output + : path.resolve(process.cwd(), this.config.output); + ffmpegProcess.on('close', () => { + if (processError) { + return; + } + this.config.cb(outputPath, null); + }); + ffmpegProcess.on('error', (error) => { + if (processError) { + return; + } + processError = error; + this.config.cb(outputPath, error); + }); + ffmpegProcess.stdin.on('error', (error) => { + if (processError) { + return; + } + processError = error; + this.config.cb(outputPath, error); + }); + } + + private async finishRecording() { + this.state = 'closed'; + await this.browser.close(); + } +} + +export function transformToVideo(config: RRvideoConfig): Promise { + return new Promise((resolve, reject) => { + const rrvideo = new RRvideo({ + ...config, + cb(file, error) { + if (error) { + return reject(error); + } + resolve(file); + }, + }); + void rrvideo.transform(); + }); +} diff --git a/packages/rrvideo/tsconfig.json b/packages/rrvideo/tsconfig.json new file mode 100644 index 0000000000..ea6d53e2ca --- /dev/null +++ b/packages/rrvideo/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES6", + "module": "commonjs", + "declaration": true, + "sourceMap": true, + "outDir": "./build", + "rootDir": "./src", + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "references": [ + { + "path": "../rrweb-player" + }, + { + "path": "../types" + } + ] +} diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index 2f223369f7..a45baca485 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -20,8 +20,7 @@ "svelte": "^3.2.0", "svelte-check": "^1.4.0", "svelte-preprocess": "^4.0.0", - "tslib": "^2.0.0", - "typescript": "^4.7.3" + "tslib": "^2.0.0" }, "dependencies": { "@tsconfig/svelte": "^1.0.0", diff --git a/packages/rrweb-player/src/Controller.svelte b/packages/rrweb-player/src/Controller.svelte index 1025e45386..57d323133e 100644 --- a/packages/rrweb-player/src/Controller.svelte +++ b/packages/rrweb-player/src/Controller.svelte @@ -220,7 +220,7 @@ export const playRange = ( timeOffset: number, endTimeOffset: number, - startLooping: boolean = false, + startLooping = false, afterHook: undefined | (() => void) = undefined, ) => { if (startLooping) { diff --git a/packages/rrweb-player/src/Player.svelte b/packages/rrweb-player/src/Player.svelte index feb9dfec15..4e7d5825dc 100644 --- a/packages/rrweb-player/src/Player.svelte +++ b/packages/rrweb-player/src/Player.svelte @@ -121,7 +121,7 @@ export const playRange = ( timeOffset: number, endTimeOffset: number, - startLooping: boolean = false, + startLooping = false, afterHook: undefined | (() => void) = undefined, ) => { controller.playRange(timeOffset, endTimeOffset, startLooping, afterHook); diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index e6a782a829..225dd70362 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -56,7 +56,6 @@ "rollup-plugin-typescript2": "^0.31.2", "ts-jest": "^27.0.5", "ts-node": "^7.0.1", - "tslib": "^1.9.3", - "typescript": "^4.7.3" + "tslib": "^1.9.3" } } diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 9b4b2008a5..0add67b8f2 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -74,8 +74,7 @@ "simple-peer-light": "^9.10.0", "ts-jest": "^27.1.3", "ts-node": "^10.9.1", - "tslib": "^2.3.1", - "typescript": "^4.7.3" + "tslib": "^2.3.1" }, "dependencies": { "@rrweb/types": "^2.0.0-alpha.6", diff --git a/packages/types/package.json b/packages/types/package.json index d66bf75a98..e0b51884ff 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -39,7 +39,6 @@ "dist" ], "devDependencies": { - "typescript": "^4.7.3", "vite": "^3.2.0-beta.2", "vite-plugin-dts": "^1.6.6" }, diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index cda55bfb71..6699414a23 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -22,7 +22,6 @@ "@vitejs/plugin-react": "^2.1.0", "cross-env": "^7.0.3", "type-fest": "^2.19.0", - "typescript": "^4.7.3", "vite": "^3.1.8", "vite-plugin-web-extension": "^1.4.5", "vite-plugin-zip-pack": "^1.0.5", diff --git a/packages/web-extension/tsconfig.json b/packages/web-extension/tsconfig.json index 999bfaa228..6eba04af10 100644 --- a/packages/web-extension/tsconfig.json +++ b/packages/web-extension/tsconfig.json @@ -1,15 +1,13 @@ { "compilerOptions": { + "composite": true, "baseUrl": ".", "module": "ESNext", "target": "es2016", - "lib": [ - "DOM", - "ESNext" - ], + "lib": ["DOM", "ESNext"], "strict": true, "esModuleInterop": true, - "incremental": false, + "incremental": true, "skipLibCheck": true, "moduleResolution": "node", "resolveJsonModule": true, @@ -17,16 +15,11 @@ "strictNullChecks": true, "forceConsistentCasingInFileNames": true, "paths": { - "~/*": [ - "src/*" - ] + "~/*": ["src/*"] }, "jsx": "react-jsx" }, - "exclude": [ - "dist", - "node_modules" - ], + "exclude": ["dist", "node_modules"], "references": [ { "path": "../rrweb" diff --git a/tsconfig.json b/tsconfig.json index 68584c1002..5334be81bb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,12 @@ }, { "path": "packages/types" + }, + { + "path": "packages/rrvideo" + }, + { + "path": "packages/web-extension" } ], "files": [], diff --git a/yarn.lock b/yarn.lock index bd7f23199f..2ecefecc30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1920,11 +1920,6 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@hutson/parse-repository-url@^3.0.0": - version "3.0.2" - resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" - integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -2334,677 +2329,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@lerna/add@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz" - integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== - dependencies: - "@lerna/bootstrap" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - npm-package-arg "^8.1.0" - p-map "^4.0.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/bootstrap@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-4.0.0.tgz" - integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/has-npm-version" "4.0.0" - "@lerna/npm-install" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - get-port "^5.1.1" - multimatch "^5.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - read-package-tree "^5.3.1" - semver "^7.3.4" - -"@lerna/changed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/changed/-/changed-4.0.0.tgz" - integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== - dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/check-working-tree@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz" - integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== - dependencies: - "@lerna/collect-uncommitted" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/validation-error" "4.0.0" - -"@lerna/child-process@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-4.0.0.tgz" - integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== - dependencies: - chalk "^4.1.0" - execa "^5.0.0" - strong-log-transformer "^2.1.0" - -"@lerna/clean@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/clean/-/clean-4.0.0.tgz" - integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - p-waterfall "^2.1.1" - -"@lerna/cli@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/cli/-/cli-4.0.0.tgz" - integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== - dependencies: - "@lerna/global-options" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" - yargs "^16.2.0" - -"@lerna/collect-uncommitted@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz" - integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== - dependencies: - "@lerna/child-process" "4.0.0" - chalk "^4.1.0" - npmlog "^4.1.2" - -"@lerna/collect-updates@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-4.0.0.tgz" - integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/describe-ref" "4.0.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - slash "^3.0.0" - -"@lerna/command@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/command/-/command-4.0.0.tgz" - integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/project" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/write-log-file" "4.0.0" - clone-deep "^4.0.1" - dedent "^0.7.0" - execa "^5.0.0" - is-ci "^2.0.0" - npmlog "^4.1.2" - -"@lerna/conventional-commits@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz" - integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== - dependencies: - "@lerna/validation-error" "4.0.0" - conventional-changelog-angular "^5.0.12" - conventional-changelog-core "^4.2.2" - conventional-recommended-bump "^6.1.0" - fs-extra "^9.1.0" - get-stream "^6.0.0" - lodash.template "^4.5.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - semver "^7.3.4" - -"@lerna/create-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-4.0.0.tgz" - integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== - dependencies: - cmd-shim "^4.1.0" - fs-extra "^9.1.0" - npmlog "^4.1.2" - -"@lerna/create@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create/-/create-4.0.0.tgz" - integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - fs-extra "^9.1.0" - globby "^11.0.2" - init-package-json "^2.0.2" - npm-package-arg "^8.1.0" - p-reduce "^2.1.0" - pacote "^11.2.6" - pify "^5.0.0" - semver "^7.3.4" - slash "^3.0.0" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - whatwg-url "^8.4.0" - yargs-parser "20.2.4" - -"@lerna/describe-ref@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-4.0.0.tgz" - integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== - dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" - -"@lerna/diff@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/diff/-/diff-4.0.0.tgz" - integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/validation-error" "4.0.0" - npmlog "^4.1.2" - -"@lerna/exec@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/exec/-/exec-4.0.0.tgz" - integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" - -"@lerna/filter-options@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-4.0.0.tgz" - integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== - dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/filter-packages" "4.0.0" - dedent "^0.7.0" - npmlog "^4.1.2" - -"@lerna/filter-packages@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-4.0.0.tgz" - integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== - dependencies: - "@lerna/validation-error" "4.0.0" - multimatch "^5.0.0" - npmlog "^4.1.2" - -"@lerna/get-npm-exec-opts@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz" - integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== - dependencies: - npmlog "^4.1.2" - -"@lerna/get-packed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-4.0.0.tgz" - integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== - dependencies: - fs-extra "^9.1.0" - ssri "^8.0.1" - tar "^6.1.0" - -"@lerna/github-client@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-4.0.0.tgz" - integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== - dependencies: - "@lerna/child-process" "4.0.0" - "@octokit/plugin-enterprise-rest" "^6.0.1" - "@octokit/rest" "^18.1.0" - git-url-parse "^11.4.4" - npmlog "^4.1.2" - -"@lerna/gitlab-client@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz" - integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== - dependencies: - node-fetch "^2.6.1" - npmlog "^4.1.2" - whatwg-url "^8.4.0" - -"@lerna/global-options@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-4.0.0.tgz" - integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== - -"@lerna/has-npm-version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz" - integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== - dependencies: - "@lerna/child-process" "4.0.0" - semver "^7.3.4" - -"@lerna/import@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/import/-/import-4.0.0.tgz" - integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/validation-error" "4.0.0" - dedent "^0.7.0" - fs-extra "^9.1.0" - p-map-series "^2.1.0" - -"@lerna/info@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/info/-/info-4.0.0.tgz" - integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/output" "4.0.0" - envinfo "^7.7.4" - -"@lerna/init@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/init/-/init-4.0.0.tgz" - integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/link@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/link/-/link-4.0.0.tgz" - integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - p-map "^4.0.0" - slash "^3.0.0" - -"@lerna/list@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/list/-/list-4.0.0.tgz" - integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" - -"@lerna/listable@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/listable/-/listable-4.0.0.tgz" - integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== - dependencies: - "@lerna/query-graph" "4.0.0" - chalk "^4.1.0" - columnify "^1.5.4" - -"@lerna/log-packed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-4.0.0.tgz" - integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== - dependencies: - byte-size "^7.0.0" - columnify "^1.5.4" - has-unicode "^2.0.1" - npmlog "^4.1.2" - -"@lerna/npm-conf@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-4.0.0.tgz" - integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== - dependencies: - config-chain "^1.1.12" - pify "^5.0.0" - -"@lerna/npm-dist-tag@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz" - integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== - dependencies: - "@lerna/otplease" "4.0.0" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - -"@lerna/npm-install@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-4.0.0.tgz" - integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - fs-extra "^9.1.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - signal-exit "^3.0.3" - write-pkg "^4.0.0" - -"@lerna/npm-publish@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-4.0.0.tgz" - integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== - dependencies: - "@lerna/otplease" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - fs-extra "^9.1.0" - libnpmpublish "^4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - pify "^5.0.0" - read-package-json "^3.0.0" - -"@lerna/npm-run-script@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz" - integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== - dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" - npmlog "^4.1.2" - -"@lerna/otplease@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-4.0.0.tgz" - integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== - dependencies: - "@lerna/prompt" "4.0.0" - -"@lerna/output@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/output/-/output-4.0.0.tgz" - integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== - dependencies: - npmlog "^4.1.2" - -"@lerna/pack-directory@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-4.0.0.tgz" - integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== - dependencies: - "@lerna/get-packed" "4.0.0" - "@lerna/package" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - npm-packlist "^2.1.4" - npmlog "^4.1.2" - tar "^6.1.0" - temp-write "^4.0.0" - -"@lerna/package-graph@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-4.0.0.tgz" - integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== - dependencies: - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/validation-error" "4.0.0" - npm-package-arg "^8.1.0" - npmlog "^4.1.2" - semver "^7.3.4" - -"@lerna/package@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package/-/package-4.0.0.tgz" - integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== - dependencies: - load-json-file "^6.2.0" - npm-package-arg "^8.1.0" - write-pkg "^4.0.0" - -"@lerna/prerelease-id-from-version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz" - integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== - dependencies: - semver "^7.3.4" - -"@lerna/profiler@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-4.0.0.tgz" - integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - upath "^2.0.1" - -"@lerna/project@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/project/-/project-4.0.0.tgz" - integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== - dependencies: - "@lerna/package" "4.0.0" - "@lerna/validation-error" "4.0.0" - cosmiconfig "^7.0.0" - dedent "^0.7.0" - dot-prop "^6.0.1" - glob-parent "^5.1.1" - globby "^11.0.2" - load-json-file "^6.2.0" - npmlog "^4.1.2" - p-map "^4.0.0" - resolve-from "^5.0.0" - write-json-file "^4.3.0" - -"@lerna/prompt@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-4.0.0.tgz" - integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== - dependencies: - inquirer "^7.3.3" - npmlog "^4.1.2" - -"@lerna/publish@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/publish/-/publish-4.0.0.tgz" - integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/log-packed" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/npm-dist-tag" "4.0.0" - "@lerna/npm-publish" "4.0.0" - "@lerna/otplease" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/pack-directory" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/version" "4.0.0" - fs-extra "^9.1.0" - libnpmaccess "^4.0.1" - npm-package-arg "^8.1.0" - npm-registry-fetch "^9.0.0" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - pacote "^11.2.6" - semver "^7.3.4" - -"@lerna/pulse-till-done@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz" - integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== - dependencies: - npmlog "^4.1.2" - -"@lerna/query-graph@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-4.0.0.tgz" - integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== - dependencies: - "@lerna/package-graph" "4.0.0" - -"@lerna/resolve-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz" - integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== - dependencies: - fs-extra "^9.1.0" - npmlog "^4.1.2" - read-cmd-shim "^2.0.0" - -"@lerna/rimraf-dir@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz" - integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== - dependencies: - "@lerna/child-process" "4.0.0" - npmlog "^4.1.2" - path-exists "^4.0.0" - rimraf "^3.0.2" - -"@lerna/run-lifecycle@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz" - integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== - dependencies: - "@lerna/npm-conf" "4.0.0" - npm-lifecycle "^3.1.5" - npmlog "^4.1.2" - -"@lerna/run-topologically@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-4.0.0.tgz" - integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== - dependencies: - "@lerna/query-graph" "4.0.0" - p-queue "^6.6.2" - -"@lerna/run@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run/-/run-4.0.0.tgz" - integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== - dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-run-script" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/timer" "4.0.0" - "@lerna/validation-error" "4.0.0" - p-map "^4.0.0" - -"@lerna/symlink-binary@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz" - integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/package" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - -"@lerna/symlink-dependencies@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz" - integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== - dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/resolve-symlink" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - fs-extra "^9.1.0" - p-map "^4.0.0" - p-map-series "^2.1.0" - -"@lerna/timer@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/timer/-/timer-4.0.0.tgz" - integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== - -"@lerna/validation-error@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-4.0.0.tgz" - integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== - dependencies: - npmlog "^4.1.2" - -"@lerna/version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/version/-/version-4.0.0.tgz" - integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== - dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/conventional-commits" "4.0.0" - "@lerna/github-client" "4.0.0" - "@lerna/gitlab-client" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - chalk "^4.1.0" - dedent "^0.7.0" - load-json-file "^6.2.0" - minimatch "^3.0.4" - npmlog "^4.1.2" - p-map "^4.0.0" - p-pipe "^3.1.0" - p-reduce "^2.1.0" - p-waterfall "^2.1.1" - semver "^7.3.4" - slash "^3.0.0" - temp-write "^4.0.0" - write-json-file "^4.3.0" - -"@lerna/write-log-file@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-4.0.0.tgz" - integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== - dependencies: - npmlog "^4.1.2" - write-file-atomic "^3.0.3" - "@manypkg/find-root@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@manypkg/find-root/-/find-root-1.1.0.tgz#a62d8ed1cd7e7d4c11d9d52a8397460b5d4ad29f" @@ -3193,170 +2517,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/ci-detect@^1.0.0": - version "1.3.0" - resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz" - integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== - -"@npmcli/git@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz" - integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== - dependencies: - "@npmcli/promise-spawn" "^1.3.2" - lru-cache "^6.0.0" - mkdirp "^1.0.4" - npm-pick-manifest "^6.1.1" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^2.0.2" - -"@npmcli/installed-package-contents@^1.0.6": - version "1.0.7" - resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz" - integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== - dependencies: - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" - -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - -"@npmcli/node-gyp@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz" - integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg== - -"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": - version "1.3.2" - resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz" - integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== - dependencies: - infer-owner "^1.0.4" - -"@npmcli/run-script@^1.8.2": - version "1.8.5" - resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz" - integrity sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A== - dependencies: - "@npmcli/node-gyp" "^1.0.2" - "@npmcli/promise-spawn" "^1.3.2" - infer-owner "^1.0.4" - node-gyp "^7.1.0" - read-package-json-fast "^2.0.1" - -"@octokit/auth-token@^2.4.4": - version "2.4.5" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz" - integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.5.0": - version "3.5.1" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.6.4" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.4.tgz" - integrity sha512-SWTdXsVheRmlotWNjKzPOb6Js6tjSqA2a8z9+glDJng0Aqjzti8MEWOtuT8ZSu6wHnci7LZNuarE87+WJBG4vg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^9.5.0": - version "9.7.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz" - integrity sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg== - -"@octokit/plugin-enterprise-rest@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz" - integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== - -"@octokit/plugin-paginate-rest@^2.6.2": - version "2.15.1" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz" - integrity sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg== - dependencies: - "@octokit/types" "^6.24.0" - -"@octokit/plugin-request-log@^1.0.2": - version "1.0.4" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@5.7.0": - version "5.7.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.7.0.tgz" - integrity sha512-G7sgccWRYQMwcHJXkDY/sDxbXeKiZkFQqUtzBCwmrzCNj2GQf3VygQ4T/BFL2crLVpIbenkE/c0ErhYOte2MPw== - dependencies: - "@octokit/types" "^6.24.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.6.0": - version "5.6.1" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz" - integrity sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.1.0": - version "18.9.0" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.9.0.tgz" - integrity sha512-VrmrE8gjpuOoDAGjrQq2j9ZhOE6LxaqxaQg0yMrrEnnQZy2ZcAnr5qbVfKsMF0up/48PRV/VFS/2GSMhA7nTdA== - dependencies: - "@octokit/core" "^3.5.0" - "@octokit/plugin-paginate-rest" "^2.6.2" - "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.7.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.24.0": - version "6.25.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz" - integrity sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q== - dependencies: - "@octokit/openapi-types" "^9.5.0" - "@polka/url@^0.5.0": version "0.5.0" resolved "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz" @@ -3742,7 +2902,7 @@ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== -"@types/minimist@^1.2.0": +"@types/minimist@^1.2.0", "@types/minimist@^1.2.1": version "1.2.2" resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== @@ -4157,24 +3317,11 @@ resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-0.1.0.tgz#9777bbaff8316d0b3a14a9095631e1494f69dbc7" integrity sha512-PeaBcTmdZWcFf7n1aM+oiOdZc+sy14qi0emPIeUuGMTjbP0xLGrZu43kdpHnWSXy7/r4Ubp/vlg50MCV8+9Isg== -JSONStream@^1.0.4: - version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.7: version "1.3.7" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" @@ -4221,11 +3368,6 @@ acorn@^8.7.0, acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== -add-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz" - integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= - addons-linter@4.14.0: version "4.14.0" resolved "https://registry.yarnpkg.com/addons-linter/-/addons-linter-4.14.0.tgz#e8a5b99665a7e7df3fda2da42d73593fea8db3f9" @@ -4285,30 +3427,13 @@ adm-zip@~0.5.x: resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.9.tgz#b33691028333821c0cf95c31374c5462f2905a83" integrity sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg== -agent-base@6, agent-base@^6.0.2: +agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" -agentkeepalive@^4.1.3: - version "4.1.4" - resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz" - integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== - dependencies: - debug "^4.1.0" - depd "^1.1.2" - humanize-ms "^1.2.1" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - ajv-merge-patch@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ajv-merge-patch/-/ajv-merge-patch-4.1.0.tgz#cd580e5860ac53431d6aa901fa3d5e2eb2b74a6c" @@ -4430,24 +3555,6 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -aproba@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - arg@^4.1.0: version "4.1.3" resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" @@ -4494,11 +3601,6 @@ array-flatten@1.1.1: resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-ify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" - integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= - array-union@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" @@ -4541,11 +3643,6 @@ arrify@^2.0.1: resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0: - version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - asn1@~0.2.3: version "0.2.4" resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" @@ -4758,11 +3855,6 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - better-path-resolve@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" @@ -4991,11 +4083,6 @@ builtin-modules@^3.1.0: resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz" integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== -builtins@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz" - integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= - bunyan@1.8.15: version "1.8.15" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" @@ -5006,44 +4093,11 @@ bunyan@1.8.15: mv "~2" safe-json-stringify "~1" -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" - integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= - -byte-size@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/byte-size/-/byte-size-7.0.1.tgz" - integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== - bytes@3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^15.0.5, cacache@^15.2.0: - version "15.2.0" - resolved "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz" - integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.0.2" - unique-filename "^1.1.1" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -5223,16 +4277,11 @@ chokidar@^3.4.1, chokidar@^3.5.0: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1, chownr@^1.1.4: +chownr@^1.1.1: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - chrome-launcher@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.0.tgz#5144a57aba0cf2f4cbe61dccefdde024fb3ca7fc" @@ -5243,6 +4292,13 @@ chrome-launcher@0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" +chromium-bidi@0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.5.tgz#a352e755536dde609bd2c77e4b1f0906bff8784e" + integrity sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg== + dependencies: + mitt "3.0.0" + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" @@ -5268,23 +4324,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - cli-cursor@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" @@ -5297,11 +4341,6 @@ cli-spinners@^2.6.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cli-width@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" @@ -5334,15 +4373,6 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone-response@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" @@ -5355,13 +4385,6 @@ clone@^1.0.2: resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -cmd-shim@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz" - integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw== - dependencies: - mkdirp-infer-owner "^2.0.0" - co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" @@ -5381,11 +4404,6 @@ code-block-writer@^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-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" @@ -5449,14 +4467,6 @@ columnify@1.6.0: strip-ansi "^6.0.1" wcwidth "^1.0.0" -columnify@^1.5.4: - version "1.5.4" - resolved "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" - integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= - dependencies: - strip-ansi "^3.0.0" - wcwidth "^1.0.0" - combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -5501,14 +4511,6 @@ commondir@^1.0.1: resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -compare-func@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" - integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== - dependencies: - array-ify "^1.0.0" - dot-prop "^5.1.0" - compare-versions@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" @@ -5534,16 +4536,6 @@ concat-stream@^1.4.7: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - concat-with-sourcemaps@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz" @@ -5565,14 +4557,6 @@ concurrently@^7.1.0: tree-kill "^1.2.2" yargs "^16.2.0" -config-chain@^1.1.12: - version "1.1.13" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -5590,11 +4574,6 @@ console-clear@^1.1.0: resolved "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz" integrity sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - construct-style-sheets-polyfill@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.1.0.tgz#c490abd79efdb359fafa62ec14ea55232be0eecf" @@ -5612,89 +4591,6 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -conventional-changelog-angular@^5.0.12: - version "5.0.12" - resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz" - integrity sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-core@^4.2.2: - version "4.2.3" - resolved "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.3.tgz" - integrity sha512-MwnZjIoMRL3jtPH5GywVNqetGILC7g6RQFvdb8LRU/fA/338JbeWAku3PZ8yQ+mtVRViiISqJlb0sOz0htBZig== - dependencies: - add-stream "^1.0.0" - conventional-changelog-writer "^5.0.0" - conventional-commits-parser "^3.2.0" - dateformat "^3.0.0" - get-pkg-repo "^4.0.0" - git-raw-commits "^2.0.8" - git-remote-origin-url "^2.0.0" - git-semver-tags "^4.1.1" - lodash "^4.17.15" - normalize-package-data "^3.0.0" - q "^1.5.1" - read-pkg "^3.0.0" - read-pkg-up "^3.0.0" - through2 "^4.0.0" - -conventional-changelog-preset-loader@^2.3.4: - version "2.3.4" - resolved "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz" - integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== - -conventional-changelog-writer@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz" - integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== - dependencies: - conventional-commits-filter "^2.0.7" - dateformat "^3.0.0" - handlebars "^4.7.6" - json-stringify-safe "^5.0.1" - lodash "^4.17.15" - meow "^8.0.0" - semver "^6.0.0" - split "^1.0.0" - through2 "^4.0.0" - -conventional-commits-filter@^2.0.7: - version "2.0.7" - resolved "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz" - integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== - dependencies: - lodash.ismatch "^4.4.0" - modify-values "^1.0.0" - -conventional-commits-parser@^3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.1.tgz" - integrity sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA== - dependencies: - JSONStream "^1.0.4" - is-text-path "^1.0.1" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - trim-off-newlines "^1.0.0" - -conventional-recommended-bump@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz" - integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== - dependencies: - concat-stream "^2.0.0" - conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.7" - conventional-commits-parser "^3.2.0" - git-raw-commits "^2.0.8" - git-semver-tags "^4.1.1" - meow "^8.0.0" - q "^1.5.1" - convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" @@ -5749,6 +4645,16 @@ core-util-is@^1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cosmiconfig@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.0.tgz#947e174c796483ccf0a48476c24e4fefb7e1aea8" + integrity sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" @@ -6058,11 +4964,6 @@ csv@^5.5.0: csv-stringify "^5.6.5" stream-transform "^2.1.3" -dargs@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" - integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" @@ -6089,11 +4990,6 @@ date-fns@^2.16.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== -dateformat@^3.0.0: - version "3.0.3" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== - debounce@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" @@ -6120,11 +5016,6 @@ debug@4.3.4, debug@^4.3.3, debug@^4.3.4: dependencies: ms "2.1.2" -debuglog@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" - integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= - decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz" @@ -6148,11 +5039,6 @@ decimal.js@^10.2.1: resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== -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" - integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== - decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -6231,31 +5117,16 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@^1.1.2, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - destroy@~1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-indent@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz" - integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= - detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" @@ -6276,6 +5147,11 @@ devtools-protocol@0.0.1036444: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1036444.tgz#a570d3cdde61527c82f9b03919847b8ac7b1c2b9" integrity sha512-0y4f/T8H9lsESV9kKP1HDUXgHxCdniFeJh6Erq+FbdOEvp/Ydp9t8kcAAM5gOd17pMrTDlFWntoHtzzeTUWKNw== +devtools-protocol@0.0.1094867: + version "0.0.1094867" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz#2ab93908e9376bd85d4e0604aa2651258f13e374" + integrity sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ== + devtools-protocol@0.0.869402: version "0.0.869402" resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" @@ -6286,14 +5162,6 @@ devtools-protocol@0.0.901419: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== -dezalgo@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz" - integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= - dependencies: - asap "^2.0.0" - wrappy "1" - diff-sequences@^27.0.6: version "27.0.6" resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz" @@ -6386,20 +5254,13 @@ domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: domelementtype "^2.2.0" domhandler "^4.2.0" -dot-prop@^5.1.0, dot-prop@^5.2.0: +dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" -dot-prop@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz" - integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== - dependencies: - is-obj "^2.0.0" - dotenv@^8.1.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -6417,11 +5278,6 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== -duplexer@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" - integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== - duplexify@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" @@ -6502,13 +5358,6 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.12: - version "0.1.13" - resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@~1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" @@ -6538,21 +5387,6 @@ entities@~2.1.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -envinfo@^7.7.4: - version "7.8.1" - resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== - -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" @@ -7535,13 +6369,6 @@ fflate@^0.4.4: resolved "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz" integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - figures@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/figures/-/figures-4.0.1.tgz#27b26609907bc888b3e3b0ef5403643f80aa2518" @@ -7594,11 +6421,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" @@ -7626,13 +6448,6 @@ find-root@^1.1.0: resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" @@ -7783,7 +6598,7 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@9.1.0, fs-extra@^9.1.0: +fs-extra@9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -7839,20 +6654,6 @@ fs-extra@~9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - -fs-minipass@^2.0.0, fs-minipass@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -7900,20 +6701,6 @@ fx-runner@1.2.0: which "1.2.4" winreg "0.0.12" -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - generic-names@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz" @@ -7968,26 +6755,11 @@ get-package-type@^0.1.0: resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-pkg-repo@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.1.2.tgz" - integrity sha512-/FjamZL9cBYllEbReZkxF2IMh80d8TJoC4e3bmLNif8ibHw95aj0N/tzqK0kZz9eU/3w3dL6lF4fnnX/sDdW3A== - dependencies: - "@hutson/parse-repository-url" "^3.0.0" - hosted-git-info "^4.0.0" - meow "^7.0.0" - through2 "^2.0.0" - get-port@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz" @@ -8017,69 +6789,20 @@ get-stream@^6.0.0: resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -git-raw-commits@^2.0.8: - version "2.0.10" - resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz" - integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== - dependencies: - dargs "^7.0.0" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -git-remote-origin-url@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz" - integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= - dependencies: - gitconfiglocal "^1.0.0" - pify "^2.3.0" - -git-semver-tags@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz" - integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== - dependencies: - meow "^8.0.0" - semver "^6.0.0" - -git-up@^4.0.0: - version "4.0.5" - resolved "https://registry.npmjs.org/git-up/-/git-up-4.0.5.tgz" - integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== - dependencies: - is-ssh "^1.3.0" - parse-url "^6.0.0" - -git-url-parse@^11.4.4: - version "11.5.0" - resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.5.0.tgz" - integrity sha512-TZYSMDeM37r71Lqg1mbnMlOqlHd7BSij9qN7XwTkRqSAYFMihGLGhfHwgqQob3GUhEneKnV4nskN9rbQw2KGxA== +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: - git-up "^4.0.0" + call-bind "^1.0.2" + get-intrinsic "^1.1.1" -gitconfiglocal@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz" - integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: - ini "^1.3.2" + assert-plus "^1.0.0" glob-base@^0.3.0: version "0.3.0" @@ -8096,7 +6819,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -8150,6 +6873,16 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^9.2.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" + integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== + dependencies: + fs.realpath "^1.0.0" + minimatch "^7.4.1" + minipass "^4.2.4" + path-scurry "^1.6.1" + glob@~7.2.0: version "7.2.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.2.tgz#29deb38e1ef90f132d5958abe9c3ee8e87f3c318" @@ -8200,7 +6933,7 @@ globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^11.0.2, globby@^11.0.4: +globby@^11.0.4: version "11.0.4" resolved "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -8252,16 +6985,16 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.2.6" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.1.2: + version "4.2.6" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + graceful-fs@^4.1.5: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -8287,18 +7020,6 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== -handlebars@^4.7.6: - version "4.7.7" - resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" @@ -8388,11 +7109,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0, has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -8427,13 +7143,6 @@ hosted-git-info@^2.1.4: resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz" - integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== - dependencies: - lru-cache "^6.0.0" - hsl-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz" @@ -8466,7 +7175,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -8542,13 +7251,6 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= - dependencies: - ms "^2.0.0" - iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -8556,13 +7258,6 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 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== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz" @@ -8590,13 +7285,6 @@ ignore-styles@^5.0.1: resolved "https://registry.npmjs.org/ignore-styles/-/ignore-styles-5.0.1.tgz" integrity sha1-tJ7yJ0va/NikiAqWa/440aC/RnE= -ignore-walk@^3.0.3: - version "3.0.4" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz" - integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== - dependencies: - minimatch "^3.0.4" - ignore@^5.1.4: version "5.1.8" resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" @@ -8696,11 +7384,6 @@ indexes-of@^1.0.1: resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" @@ -8724,44 +7407,11 @@ ini@2.0.0, ini@~2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: +ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -init-package-json@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-2.0.3.tgz" - integrity sha512-tk/gAgbMMxR6fn1MgMaM1HpU1ryAmBWWitnxG5OhuNXeX0cbpbgV5jA4AIpQJVNoyOfOevTtO6WX+rPs+EFqaQ== - dependencies: - glob "^7.1.1" - npm-package-arg "^8.1.2" - promzard "^0.3.0" - read "~1.0.1" - read-package-json "^3.0.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "^3.0.0" - -inquirer@^7.3.3: - version "7.3.3" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - inquirer@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.0.2.tgz#81d830044718528485d7b9f7d47c6d590ccd1a7f" @@ -8813,11 +7463,6 @@ invert-kv@^3.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523" integrity sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw== -ip@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" @@ -8997,18 +7642,6 @@ is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" @@ -9046,11 +7679,6 @@ is-interactive@^2.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz" - integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= - is-mergeable-object@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-mergeable-object/-/is-mergeable-object-1.1.1.tgz#faaa3ed1cfce87d6f7d2f5885e92cc30af3e2ebf" @@ -9108,28 +7736,11 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: +is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz" @@ -9190,13 +7801,6 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-ssh@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" - integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== - dependencies: - protocols "^2.0.1" - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" @@ -9228,13 +7832,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-text-path@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" - integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= - dependencies: - text-extensions "^1.0.0" - is-typed-array@^1.1.10, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" @@ -9314,11 +7911,6 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" @@ -10437,7 +9029,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -10482,11 +9074,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@^1.2.0, jsonparse@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - jsonwebtoken@8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -10554,7 +9141,7 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.0, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -10588,30 +9175,6 @@ lcid@^3.0.0: dependencies: invert-kv "^3.0.0" -lerna@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz" - integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== - dependencies: - "@lerna/add" "4.0.0" - "@lerna/bootstrap" "4.0.0" - "@lerna/changed" "4.0.0" - "@lerna/clean" "4.0.0" - "@lerna/cli" "4.0.0" - "@lerna/create" "4.0.0" - "@lerna/diff" "4.0.0" - "@lerna/exec" "4.0.0" - "@lerna/import" "4.0.0" - "@lerna/info" "4.0.0" - "@lerna/init" "4.0.0" - "@lerna/link" "4.0.0" - "@lerna/list" "4.0.0" - "@lerna/publish" "4.0.0" - "@lerna/run" "4.0.0" - "@lerna/version" "4.0.0" - import-local "^3.0.2" - npmlog "^4.1.2" - leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -10633,27 +9196,6 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libnpmaccess@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-4.0.3.tgz" - integrity sha512-sPeTSNImksm8O2b6/pf3ikv4N567ERYEpeKRPSmqlNt1dTZbvgpJIzg5vAhXHpw2ISBsELFRelk0jEahj1c6nQ== - dependencies: - aproba "^2.0.0" - minipass "^3.1.1" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" - -libnpmpublish@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-4.0.2.tgz" - integrity sha512-+AD7A2zbVeGRCFI2aO//oUmapCwy7GHqPXFJh3qpToSRNU+tXKJ2YFUgjt04LPPAf2dlEH95s6EhIHM1J7bmOw== - dependencies: - normalize-package-data "^3.0.2" - npm-package-arg "^8.1.2" - npm-registry-fetch "^11.0.0" - semver "^7.1.3" - ssri "^8.0.1" - lie@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" @@ -10696,16 +9238,6 @@ livereload@^0.9.1: opts ">= 1.2.0" ws "^7.4.3" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - load-json-file@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz" @@ -10740,14 +9272,6 @@ local-access@^1.0.1: resolved "https://registry.npmjs.org/local-access/-/local-access-1.1.0.tgz" integrity sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw== -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" @@ -10762,11 +9286,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" @@ -10797,11 +9316,6 @@ lodash.isinteger@^4.0.4: resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== -lodash.ismatch@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz" - integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= - lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" @@ -10842,27 +9356,12 @@ lodash.startcase@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.15: +lodash@4.x, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.15: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10907,6 +9406,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + magic-string@^0.25.7: version "0.25.7" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" @@ -10921,14 +9425,6 @@ magic-string@^0.26.2: dependencies: sourcemap-codec "^1.4.8" -make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -10941,49 +9437,6 @@ make-error@1.x, make-error@^1.1.1, make-error@^1.3.2: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^8.0.9: - version "8.0.14" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz" - integrity sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.0.5" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - promise-retry "^2.0.1" - socks-proxy-agent "^5.0.0" - ssri "^8.0.0" - -make-fetch-happen@^9.0.1: - version "9.0.4" - resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.0.4.tgz" - integrity sha512-sQWNKMYqSmbAGXqJg2jZ+PmHh5JAybvwu0xM8mZR/bsTjGiTASj3ldXJV7KFHy1k/IJIBkjxQFoWIVsv9+PQMg== - dependencies: - agentkeepalive "^4.1.3" - cacache "^15.2.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^6.0.0" - minipass "^3.1.3" - minipass-collect "^1.0.2" - minipass-fetch "^1.3.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.2" - promise-retry "^2.0.1" - socks-proxy-agent "^5.0.0" - ssri "^8.0.0" - makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" @@ -11112,7 +9565,7 @@ meow@^6.0.0: type-fest "^0.13.1" yargs-parser "^18.1.3" -meow@^7.0.0, meow@^7.1.1: +meow@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz" integrity sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA== @@ -11129,23 +9582,6 @@ meow@^7.0.0, meow@^7.1.1: type-fest "^0.13.1" yargs-parser "^18.1.3" -meow@^8.0.0: - version "8.1.2" - resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" - integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== - dependencies: - "@types/minimist" "^1.2.0" - camelcase-keys "^6.2.2" - decamelize-keys "^1.1.0" - hard-rejection "^2.1.0" - minimist-options "4.1.0" - normalize-package-data "^3.0.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.18.0" - yargs-parser "^20.2.3" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" @@ -11264,6 +9700,13 @@ minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +minimatch@^7.4.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" + integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== + dependencies: + brace-expansion "^2.0.1" + minimatch@~3.0.5: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" @@ -11285,84 +9728,12 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: - version "1.3.4" - resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.4.tgz" - integrity sha512-TielGogIzbUEtd1LsjZFs47RWuHHfhl6TiCx1InVxApBAmQ8bL0dL5ilkLGcRvuyW/A9nE+Lvn855Ewz8S0PnQ== - dependencies: - minipass "^3.1.0" - minipass-sized "^1.0.3" - minizlib "^2.0.0" - optionalDependencies: - encoding "^0.1.12" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-json-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz" - integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== - dependencies: - jsonparse "^1.3.1" - minipass "^3.0.0" - -minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== - dependencies: - yallist "^4.0.0" - -minizlib@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - -minizlib@^2.0.0, minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" +minipass@^4.0.2, minipass@^4.2.4: + version "4.2.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" + integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== -mitt@^3.0.0: +mitt@3.0.0, 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== @@ -11377,32 +9748,18 @@ mkdirp-classic@^0.5.2: resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp-infer-owner@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz" - integrity sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw== - dependencies: - chownr "^2.0.0" - infer-owner "^1.0.4" - mkdirp "^1.0.3" - -mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@1.0.4, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -modify-values@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" - integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== - moment@^2.19.3: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" @@ -11428,12 +9785,12 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.1.1: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multimatch@5.0.0, multimatch@^5.0.0: +multimatch@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz" integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== @@ -11444,7 +9801,7 @@ multimatch@5.0.0, multimatch@^5.0.0: arrify "^2.0.1" minimatch "^3.0.4" -mute-stream@0.0.8, mute-stream@~0.0.4: +mute-stream@0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== @@ -11492,16 +9849,11 @@ ncp@~2.0.0: resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== -negotiator@0.6.2, negotiator@^0.6.2: +negotiator@0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.6.0: - version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" @@ -11533,39 +9885,6 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-gyp@^5.0.2: - version "5.1.1" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.1.tgz" - integrity sha512-WH0WKGi+a4i4DUt2mHnvocex/xPLp9pYt5R6M2JdFB7pJ7Z34hveZ4nDTGTiLXCkitA9T8HFZjhinBCiVHYcWw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.1.2" - request "^2.88.0" - rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" - -node-gyp@^7.1.0: - version "7.1.2" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.3" - nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" - rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" - which "^2.0.2" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -11608,22 +9927,7 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== -nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -11633,16 +9937,6 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0, normalize-package-data@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz" - integrity sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg== - dependencies: - hosted-git-info "^4.0.1" - resolve "^1.20.0" - semver "^7.3.4" - validate-npm-package-license "^3.0.1" - normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" @@ -11665,99 +9959,6 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -normalize-url@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -npm-bundled@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" - integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-install-checks@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz" - integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== - dependencies: - semver "^7.1.1" - -npm-lifecycle@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz" - integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== - dependencies: - byline "^5.0.0" - graceful-fs "^4.1.15" - node-gyp "^5.0.2" - resolve-from "^4.0.0" - slide "^1.1.6" - uid-number "0.0.6" - umask "^1.1.0" - which "^1.3.1" - -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-package-arg@^8.1.2: - version "8.1.5" - resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz" - integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== - dependencies: - hosted-git-info "^4.0.1" - semver "^7.3.4" - validate-npm-package-name "^3.0.0" - -npm-packlist@^2.1.4: - version "2.2.2" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz" - integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== - dependencies: - glob "^7.1.6" - ignore-walk "^3.0.3" - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" - -npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.1: - version "6.1.1" - resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz" - integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== - dependencies: - npm-install-checks "^4.0.0" - npm-normalize-package-bin "^1.0.1" - npm-package-arg "^8.1.2" - semver "^7.3.4" - -npm-registry-fetch@^11.0.0: - version "11.0.0" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz" - integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA== - dependencies: - make-fetch-happen "^9.0.1" - minipass "^3.1.3" - minipass-fetch "^1.3.0" - minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" - -npm-registry-fetch@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz" - integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== - dependencies: - "@npmcli/ci-detect" "^1.0.0" - lru-cache "^6.0.0" - make-fetch-happen "^8.0.9" - minipass "^3.1.3" - minipass-fetch "^1.3.0" - minipass-json-stream "^1.0.1" - minizlib "^2.0.0" - npm-package-arg "^8.0.0" - npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" @@ -11765,16 +9966,6 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" @@ -11789,11 +9980,6 @@ nth-check@^2.0.0: dependencies: boolbase "^1.0.0" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" @@ -11804,7 +9990,7 @@ oauth-sign@~0.9.0: resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -11857,15 +10043,6 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - object.getownpropertydescriptors@^2.1.0: version "2.1.3" resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz" @@ -11873,7 +10050,16 @@ object.getownpropertydescriptors@^2.1.0: dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.1" + es-abstract "^1.19.1" + +object.getownpropertydescriptors@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz" + integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" object.omit@^2.0.0: version "2.0.1" @@ -11970,11 +10156,6 @@ ora@^6.1.2: strip-ansi "^7.0.1" wcwidth "^1.0.1" -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-locale@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0" @@ -11989,19 +10170,11 @@ os-shim@^0.1.2: resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" integrity sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A== -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - outdent@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" @@ -12034,13 +10207,6 @@ p-is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -12055,13 +10221,6 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" @@ -12076,29 +10235,12 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map-series@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz" - integrity sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q== - p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-pipe@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz" - integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== - -p-queue@^6.3.0, p-queue@^6.6.2: +p-queue@^6.3.0: version "6.6.2" resolved "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz" integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== @@ -12106,11 +10248,6 @@ p-queue@^6.3.0, p-queue@^6.6.2: eventemitter3 "^4.0.4" p-timeout "^3.2.0" -p-reduce@^2.0.0, p-reduce@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz" - integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== - p-timeout@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz" @@ -12118,23 +10255,11 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -p-waterfall@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz" - integrity sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw== - dependencies: - p-reduce "^2.0.0" - package-json@^6.3.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" @@ -12145,31 +10270,6 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pacote@^11.2.6: - version "11.3.5" - resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz" - integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg== - dependencies: - "@npmcli/git" "^2.1.0" - "@npmcli/installed-package-contents" "^1.0.6" - "@npmcli/promise-spawn" "^1.2.0" - "@npmcli/run-script" "^1.8.2" - cacache "^15.0.5" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.3" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^2.1.4" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^11.0.0" - promise-retry "^2.0.1" - read-package-json-fast "^2.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.1.0" - pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -12210,26 +10310,6 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-path@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea" - integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw== - dependencies: - is-ssh "^1.3.0" - protocols "^1.4.0" - qs "^6.9.4" - query-string "^6.13.8" - -parse-url@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.2.tgz#4a30b057bfc452af64512dfb1a7755c103db3ea1" - integrity sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ== - dependencies: - is-ssh "^1.3.0" - normalize-url "^6.1.0" - parse-path "^4.0.4" - protocols "^1.4.0" - parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" @@ -12259,11 +10339,6 @@ path-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" @@ -12296,6 +10371,14 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" + integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + dependencies: + lru-cache "^7.14.1" + minipass "^4.0.2" + path-strip-sep@^1.0.10: version "1.0.10" resolved "https://registry.npmjs.org/path-strip-sep/-/path-strip-sep-1.0.10.tgz" @@ -12308,13 +10391,6 @@ path-to-regexp@0.1.7: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" @@ -12933,19 +11009,6 @@ progress@2.0.3, progress@^2.0.1: resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - promise-toolbox@0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/promise-toolbox/-/promise-toolbox-0.20.0.tgz#af04d7338038c2362b8fb7c27546c57d893bf562" @@ -12966,13 +11029,6 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -promzard@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz" - integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= - dependencies: - read "1" - prop-types@^15.6.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -12982,21 +11038,6 @@ prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.13.1" -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -protocols@^1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" - integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== - -protocols@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" - integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== - proxy-addr@~2.0.5: version "2.0.7" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" @@ -13040,6 +11081,23 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +puppeteer-core@19.7.5: + version "19.7.5" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.7.5.tgz#cedc8eb7862fe7a8aa2a25ed167c0f1230de72b2" + integrity sha512-EJuNha+SxPfaYFbkoWU80H3Wb1SiQH5fFyb2xdbWda0ziax5mhV63UMlqNfPeTDIWarwtR4OIcq/9VqY8HPOsg== + dependencies: + chromium-bidi "0.4.5" + cross-fetch "3.1.5" + debug "4.3.4" + devtools-protocol "0.0.1094867" + extract-zip "2.0.1" + https-proxy-agent "5.0.1" + proxy-from-env "1.1.0" + rimraf "4.4.0" + tar-fs "2.1.1" + unbzip2-stream "1.4.3" + ws "8.12.1" + puppeteer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-11.0.0.tgz#0808719c38e15315ecc1b1c28911f1c9054d201f" @@ -13075,6 +11133,17 @@ puppeteer@^17.1.3: unbzip2-stream "1.4.3" ws "8.8.1" +puppeteer@^19.7.2: + version "19.7.5" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.7.5.tgz#d7db0dfcc80ca2cdf8eb0100bae1ce888a841389" + integrity sha512-UqD8K+yaZa6/hwzP54AATCiHrEYGGxzQcse9cZzrtsVGd8wT0llCdYhsBp8n+zvnb1ofY0YFgI3TYZ/MiX5uXQ== + dependencies: + cosmiconfig "8.1.0" + https-proxy-agent "5.0.1" + progress "2.0.3" + proxy-from-env "1.1.0" + puppeteer-core "19.7.5" + puppeteer@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" @@ -13093,7 +11162,7 @@ puppeteer@^9.1.1: unbzip2-stream "^1.3.3" ws "^7.2.3" -q@^1.1.2, q@^1.5.1: +q@^1.1.2: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -13103,28 +11172,11 @@ qs@6.7.0: resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.9.4: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - qs@~6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^6.13.8: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -13297,56 +11349,6 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -read-cmd-shim@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz" - integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== - -read-package-json-fast@^2.0.1: - version "2.0.3" - resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz" - integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== - dependencies: - json-parse-even-better-errors "^2.3.0" - npm-normalize-package-bin "^1.0.1" - -read-package-json@^2.0.0: - version "2.1.2" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz" - integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^2.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-json@^3.0.0, read-package-json@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-3.0.1.tgz" - integrity sha512-aLcPqxovhJTVJcsnROuuzQvv6oziQx4zd3JvG0vGCL5MjTONUc4uJ90zCBC6R7W7oUKBNoR/F8pkyfVwlbxqng== - dependencies: - glob "^7.1.1" - json-parse-even-better-errors "^2.3.0" - normalize-package-data "^3.0.0" - npm-normalize-package-bin "^1.0.0" - -read-package-tree@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz" - integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== - dependencies: - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - util-promisify "^2.1.0" - -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" @@ -13356,15 +11358,6 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" @@ -13385,23 +11378,7 @@ read-yaml-file@^1.1.0: pify "^4.0.1" strip-bom "^3.0.0" -read@1, read@~1.0.1: - version "1.0.7" - resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" - integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= - dependencies: - mute-stream "~0.0.4" - -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: +readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -13414,15 +11391,14 @@ readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdir-scoped-modules@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz" - integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - graceful-fs "^4.1.2" - once "^1.3.0" + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" readdirp@~3.6.0: version "3.6.0" @@ -13524,7 +11500,7 @@ repeat-string@^1.5.2, repeat-string@^1.6.1: resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request@2.88.2, request@^2.88.0, request@^2.88.2: +request@2.88.2: version "2.88.2" resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -13641,14 +11617,6 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - restore-cursor@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" @@ -13657,11 +11625,6 @@ restore-cursor@^4.0.0: onetime "^5.1.0" signal-exit "^3.0.2" -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - reusify@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" @@ -13684,7 +11647,14 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" + integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== + dependencies: + glob "^9.2.0" + +rimraf@^2.6.2: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -13857,7 +11827,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.6.0, rxjs@^6.6.3: +rxjs@^6.6.3: version "6.6.7" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -13883,7 +11853,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -13912,7 +11882,7 @@ safe-stable-stringify@^2.1.0: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.0.tgz#95fadb1bcf8057a1363e11052122f5da36a69215" integrity sha512-eehKHKpab6E741ud7ZIMcXhKcP6TSIezPkNZhy5U8xC6+VvrRdUA2tMgxGxaGl4cz7c2Ew5+mg5+wNB16KQqrA== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -13950,12 +11920,12 @@ semver-match@0.1.1: dependencies: semver "^5.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.3.5, semver@7.x, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -14017,7 +11987,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== @@ -14040,13 +12010,6 @@ sha.js@2.4.11: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" @@ -14106,7 +12069,7 @@ sign-addon@3.11.0: source-map-support "0.5.21" stream-to-promise "3.0.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -14159,16 +12122,6 @@ slash@^3.0.0: resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slide@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" - integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= - -smart-buffer@^4.1.0: - version "4.2.0" - resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - smartwrap@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/smartwrap/-/smartwrap-2.0.2.tgz#7e25d3dd58b51c6ca4aba3a9e391650ea62698a4" @@ -14181,23 +12134,6 @@ smartwrap@^2.0.2: wcwidth "^1.0.1" yargs "^15.1.0" -socks-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz" - integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== - dependencies: - agent-base "^6.0.2" - debug "4" - socks "^2.3.3" - -socks@^2.3.3: - version "2.6.1" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz" - integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== - dependencies: - ip "^1.1.5" - smart-buffer "^4.1.0" - sonic-boom@^2.2.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" @@ -14205,20 +12141,6 @@ sonic-boom@^2.2.1: dependencies: atomic-sleep "^1.0.0" -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - -sort-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz" - integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== - 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" @@ -14307,30 +12229,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz" integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split2@^3.0.0: - version "3.2.2" - resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - split2@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== -split@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - split@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" @@ -14363,13 +12266,6 @@ ssim.js@^3.1.1: resolved "https://registry.npmjs.org/ssim.js/-/ssim.js-3.5.0.tgz" integrity sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g== -ssri@^8.0.0, ssri@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - stable@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" @@ -14422,11 +12318,6 @@ stream-transform@^2.1.3: dependencies: mixme "^0.5.1" -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== - string-argv@~0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -14445,23 +12336,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^4.0.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -14537,20 +12411,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: +strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -14619,15 +12486,6 @@ strip-outer@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -strong-log-transformer@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz" - integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== - dependencies: - duplexer "^0.1.1" - minimist "^1.2.0" - through "^2.3.4" - style-inject@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz" @@ -14776,47 +12634,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4.4.12: - version "4.4.19" - resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - -tar@^6.0.2, tar@^6.1.0: - version "6.1.8" - resolved "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz" - integrity sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -temp-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -temp-write@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz" - integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== - dependencies: - graceful-fs "^4.1.15" - is-stream "^2.0.0" - make-dir "^3.0.0" - temp-dir "^1.0.0" - uuid "^3.3.2" - term-size@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" @@ -14849,11 +12666,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-extensions@^1.0.0: - version "1.9.0" - resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" - integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -14885,22 +12697,7 @@ throat@^6.0.1: resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: +through@2, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -15015,11 +12812,6 @@ trim-newlines@^3.0.0: resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-off-newlines@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz" - integrity sha512-kh6Tu6GbeSNMGfrrZh6Bb/4ZEHV1QlB4xNDBeog8Y9/QwFlKTRyWvY3Fs9tRDAMZliVUwieMgEdIeL/FtqjkJg== - trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" @@ -15256,11 +13048,6 @@ type-fest@^0.13.1: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== -type-fest@^0.18.0: - version "0.18.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" - integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" @@ -15271,11 +13058,6 @@ type-fest@^0.21.3: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz" - integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== - type-fest@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" @@ -15340,21 +13122,6 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uglify-js@^3.1.4: - version "3.14.1" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz" - integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== - -uid-number@0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= - -umask@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz" - integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= - unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" @@ -15393,20 +13160,6 @@ uniqs@^2.0.0: resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -15414,11 +13167,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" @@ -15520,13 +13268,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util-promisify@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz" - integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= - dependencies: - object.getownpropertydescriptors "^2.0.3" - util.promisify@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz" @@ -15582,7 +13323,7 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: +validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -15590,13 +13331,6 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validate-npm-package-name@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz" - integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= - dependencies: - builtins "^1.0.3" - validator@^13.7.0: version "13.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" @@ -15786,7 +13520,7 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0: +whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== @@ -15844,7 +13578,7 @@ which@1.2.4: is-absolute "^0.1.7" isexe "^1.1.1" -which@^1.2.9, which@^1.3.1: +which@^1.2.9: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -15858,13 +13592,6 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -15882,11 +13609,6 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -15919,16 +13641,7 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.4.2: - version "2.4.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - -write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: +write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== @@ -15938,44 +13651,16 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -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" - integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== - dependencies: - detect-indent "^5.0.0" - graceful-fs "^4.1.15" - make-dir "^2.1.0" - pify "^4.0.1" - sort-keys "^2.0.0" - write-file-atomic "^2.4.2" - -write-json-file@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz" - integrity sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ== - dependencies: - detect-indent "^6.0.0" - graceful-fs "^4.1.15" - is-plain-obj "^2.0.0" - make-dir "^3.0.0" - sort-keys "^4.0.0" - write-file-atomic "^3.0.0" - -write-pkg@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz" - integrity sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA== - dependencies: - sort-keys "^2.0.0" - type-fest "^0.4.1" - write-json-file "^3.2.0" - ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.12.1: + version "8.12.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" + integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== + ws@8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" @@ -16029,11 +13714,6 @@ xmlchars@^2.2.0: resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -16049,11 +13729,6 @@ 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: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" @@ -16064,12 +13739,7 @@ yaml@^1.10.0: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: +yargs-parser@20.x, yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== From 1b2855bde142c107a95b8d2551502f28dd54620e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Mar 2023 19:05:18 +0200 Subject: [PATCH 162/201] Version Packages (alpha) (#1175) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 13 +++++++++++- packages/rrdom-nodejs/CHANGELOG.md | 8 ++++++++ packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/CHANGELOG.md | 7 +++++++ packages/rrdom/package.json | 6 +++--- packages/rrvideo/CHANGELOG.md | 10 ++++++++++ packages/rrvideo/package.json | 6 +++--- packages/rrweb-player/CHANGELOG.md | 7 +++++++ packages/rrweb-player/package.json | 6 +++--- packages/rrweb-snapshot/CHANGELOG.md | 10 ++++++++++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 30 ++++++++++++++++++++++++++++ packages/rrweb/package.json | 8 ++++---- packages/types/CHANGELOG.md | 7 +++++++ packages/types/package.json | 4 ++-- packages/web-extension/CHANGELOG.md | 8 ++++++++ packages/web-extension/package.json | 8 ++++---- 17 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 packages/rrvideo/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 7e86efb261..670fb54476 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -8,29 +8,40 @@ "rrweb-player": "1.0.0-alpha.4", "rrweb-snapshot": "2.0.0-alpha.4", "@rrweb/types": "2.0.0-alpha.4", - "@rrweb/web-extension": "2.0.0-alpha.4" + "@rrweb/web-extension": "2.0.0-alpha.4", + "rrvideo": "2.0.0-alpha.6" }, "changesets": [ "calm-bulldogs-speak", "chatty-cherries-train", "eight-terms-hunt", + "fast-chefs-smell", "five-peas-lay", + "fresh-spoons-drive", "grumpy-ways-own", "large-ants-prove", "loud-seals-raise", "lovely-pears-cross", "mighty-frogs-sparkle", "nervous-poets-grin", + "new-snakes-call", "old-dryers-hide", + "pretty-plums-rescue", + "proud-experts-jam", "real-masks-explode", "real-trains-switch", "serious-ants-juggle", + "sixty-impalas-laugh", + "small-olives-arrive", "stupid-ghosts-help", "swift-peas-film", "tidy-yaks-joke", + "tricky-panthers-guess", "twenty-lies-switch", "twenty-planets-repeat", "violet-melons-itch", + "violet-zebras-cry", + "yellow-mails-cheat", "young-timers-grow" ] } diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index 1348f06990..570de52da1 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,13 @@ # rrdom-nodejs +## 2.0.0-alpha.7 + +### Patch Changes + +- Updated dependencies [[`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`e7f0c80`](https://github.com/rrweb-io/rrweb/commit/e7f0c808c3f348fb27d1acd5fa300a5d92b14d00)]: + - rrweb-snapshot@2.0.0-alpha.7 + - rrdom@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index f1e3dfa873..417fa03f98 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -48,8 +48,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^2.0.0-alpha.6", - "rrweb-snapshot": "^2.0.0-alpha.6" + "rrdom": "^2.0.0-alpha.7", + "rrweb-snapshot": "^2.0.0-alpha.7" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index 9714e01ba1..25378dfe1f 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,12 @@ # rrdom +## 2.0.0-alpha.7 + +### Patch Changes + +- Updated dependencies [[`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`e7f0c80`](https://github.com/rrweb-io/rrweb/commit/e7f0c808c3f348fb27d1acd5fa300a5d92b14d00)]: + - rrweb-snapshot@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index e7ea231d6a..e477b4a12c 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.cjs", @@ -32,7 +32,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rrweb/types": "^2.0.0-alpha.6", + "@rrweb/types": "^2.0.0-alpha.7", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -47,6 +47,6 @@ "ts-jest": "^27.1.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.6" + "rrweb-snapshot": "^2.0.0-alpha.7" } } diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md new file mode 100644 index 0000000000..e77a6b447f --- /dev/null +++ b/packages/rrvideo/CHANGELOG.md @@ -0,0 +1,10 @@ +# rrvideo + +## 2.0.0-alpha.7 + +### Patch Changes + +- [#1181](https://github.com/rrweb-io/rrweb/pull/1181) [`f1f5865`](https://github.com/rrweb-io/rrweb/commit/f1f5865dcf19db5637bbb12b220eb2aa0c0219ad) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Move rrvideo to rrweb's monorepo + +- Updated dependencies []: + - rrweb-player@2.0.0-alpha.7 diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index b1a00b0b61..4ddc1a0d55 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { "name": "rrvideo", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -18,11 +18,11 @@ "license": "MIT", "devDependencies": { "@types/minimist": "^1.2.1", - "@rrweb/types": "^2.0.0-alpha.6" + "@rrweb/types": "^2.0.0-alpha.7" }, "dependencies": { "minimist": "^1.2.5", "puppeteer": "^19.7.2", - "rrweb-player": "^2.0.0-alpha.6" + "rrweb-player": "^2.0.0-alpha.7" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 0001fbf70d..4cf04e44af 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,12 @@ # rrweb-player +## 2.0.0-alpha.7 + +### Patch Changes + +- Updated dependencies [[`e0f862b`](https://github.com/rrweb-io/rrweb/commit/e0f862bac7dbaa9cfd778f5ef0f5f3fd8cbe6def), [`267e990`](https://github.com/rrweb-io/rrweb/commit/267e990dc0e45a5acaaa3ee89db7ae9171520d54), [`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`a225d8e`](https://github.com/rrweb-io/rrweb/commit/a225d8e1412a69a761c22eb45565fff0b0ce5c11), [`a82a3b4`](https://github.com/rrweb-io/rrweb/commit/a82a3b42b125aaaea607410b49f012933466c523), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`4cb4d0e`](https://github.com/rrweb-io/rrweb/commit/4cb4d0e95a540a366bdec157fe78d9f099514818)]: + - rrweb@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index a45baca485..b90a5ce3ee 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.6", + "@rrweb/types": "^2.0.0-alpha.7", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -24,7 +24,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.6" + "rrweb": "^2.0.0-alpha.7" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index 7a9d7cb8de..f4c5623e1b 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,15 @@ # rrweb-snapshot +## 2.0.0-alpha.7 + +### Minor Changes + +- [#1170](https://github.com/rrweb-io/rrweb/pull/1170) [`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3) Thanks [@mydea](https://github.com/mydea)! - feat: Ensure password inputs remain masked when switching input type + +### Patch Changes + +- [#1174](https://github.com/rrweb-io/rrweb/pull/1174) [`e7f0c80`](https://github.com/rrweb-io/rrweb/commit/e7f0c808c3f348fb27d1acd5fa300a5d92b14d00) Thanks [@wfk007](https://github.com/wfk007)! - Fix: [#1172](https://github.com/rrweb-io/rrweb/issues/1172) don't replace original onload function of Images + ## 2.0.0-alpha.6 ### Minor Changes diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 225dd70362..101b6cf24d 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index 9626f444dc..6d8ae8ecaa 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,35 @@ # rrweb +## 2.0.0-alpha.7 + +### Minor Changes + +- [#1170](https://github.com/rrweb-io/rrweb/pull/1170) [`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3) Thanks [@mydea](https://github.com/mydea)! - feat: Ensure password inputs remain masked when switching input type + +- [#1107](https://github.com/rrweb-io/rrweb/pull/1107) [`a225d8e`](https://github.com/rrweb-io/rrweb/commit/a225d8e1412a69a761c22eb45565fff0b0ce5c11) Thanks [@mydea](https://github.com/mydea)! - feat: Allow to pass `errorHandler` as record option + +### Patch Changes + +- [#1179](https://github.com/rrweb-io/rrweb/pull/1179) [`e0f862b`](https://github.com/rrweb-io/rrweb/commit/e0f862bac7dbaa9cfd778f5ef0f5f3fd8cbe6def) Thanks [@wfk007](https://github.com/wfk007)! - Fix: [#1178](https://github.com/rrweb-io/rrweb/issues/1178) remove warning related to worker_threads while building + +- [#1186](https://github.com/rrweb-io/rrweb/pull/1186) [`267e990`](https://github.com/rrweb-io/rrweb/commit/267e990dc0e45a5acaaa3ee89db7ae9171520d54) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: processed-node-manager is created even in the environment that doesn't need a recorder + +- [#1145](https://github.com/rrweb-io/rrweb/pull/1145) [`a82a3b4`](https://github.com/rrweb-io/rrweb/commit/a82a3b42b125aaaea607410b49f012933466c523) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - For a mutation which removes a node, reduce the number of spurious warnings to take into account that an anscestor (rather than just a parent) may have been just removed + +- [#1191](https://github.com/rrweb-io/rrweb/pull/1191) [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e) Thanks [@Juice10](https://github.com/Juice10)! - Only apply touch-active styling on flush + +- [#1191](https://github.com/rrweb-io/rrweb/pull/1191) [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e) Thanks [@Juice10](https://github.com/Juice10)! - Trigger mouse movement and hover with mouse up and mouse down events when replayer.pause(...) is called. + +- [#1163](https://github.com/rrweb-io/rrweb/pull/1163) [`4cb4d0e`](https://github.com/rrweb-io/rrweb/commit/4cb4d0e95a540a366bdec157fe78d9f099514818) Thanks [@zhaobosky](https://github.com/zhaobosky)! - Fix: some websites rebuild imcomplete + + 1. Some websites, addedSet in emit function is not empty, but the result converted from Array.from is empty. + 2. Some websites polyfill classList functions of HTML elements. Their implementation may throw errors and cause the snapshot to fail. I add try-catch statements to make the code robust. + +- Updated dependencies [[`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`e7f0c80`](https://github.com/rrweb-io/rrweb/commit/e7f0c808c3f348fb27d1acd5fa300a5d92b14d00)]: + - rrweb-snapshot@2.0.0-alpha.7 + - rrdom@2.0.0-alpha.7 + - @rrweb/types@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 0add67b8f2..a28dead56e 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -77,13 +77,13 @@ "tslib": "^2.3.1" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.6", + "@rrweb/types": "^2.0.0-alpha.7", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^2.0.0-alpha.6", - "rrweb-snapshot": "^2.0.0-alpha.6" + "rrdom": "^2.0.0-alpha.7", + "rrweb-snapshot": "^2.0.0-alpha.7" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index f73c42efe3..ccca53766b 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,12 @@ # @rrweb/types +## 2.0.0-alpha.7 + +### Patch Changes + +- Updated dependencies [[`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`e7f0c80`](https://github.com/rrweb-io/rrweb/commit/e7f0c808c3f348fb27d1acd5fa300a5d92b14d00)]: + - rrweb-snapshot@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index e0b51884ff..24e3873ef0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "publishConfig": { "access": "public" }, @@ -43,7 +43,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.6" + "rrweb-snapshot": "^2.0.0-alpha.7" }, "browserslist": [ "supports es6-class" diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 5abdda1b09..f3b0b83223 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,13 @@ # @rrweb/web-extension +## 2.0.0-alpha.7 + +### Patch Changes + +- Updated dependencies [[`e0f862b`](https://github.com/rrweb-io/rrweb/commit/e0f862bac7dbaa9cfd778f5ef0f5f3fd8cbe6def), [`267e990`](https://github.com/rrweb-io/rrweb/commit/267e990dc0e45a5acaaa3ee89db7ae9171520d54), [`d2582e9`](https://github.com/rrweb-io/rrweb/commit/d2582e9a81197130cd93bc1dd778e16fddfb0be3), [`a225d8e`](https://github.com/rrweb-io/rrweb/commit/a225d8e1412a69a761c22eb45565fff0b0ce5c11), [`a82a3b4`](https://github.com/rrweb-io/rrweb/commit/a82a3b42b125aaaea607410b49f012933466c523), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`1e6f71b`](https://github.com/rrweb-io/rrweb/commit/1e6f71b3cddcfafe78b9e40edfbd75e485702e4e), [`4cb4d0e`](https://github.com/rrweb-io/rrweb/commit/4cb4d0e95a540a366bdec157fe78d9f099514818)]: + - rrweb@2.0.0-alpha.7 + - rrweb-player@2.0.0-alpha.7 + ## 2.0.0-alpha.6 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 6699414a23..867cda5e0d 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0-alpha.6", + "version": "2.0.0-alpha.7", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -16,7 +16,7 @@ "prepublish": "npm run pack:chrome && npm run pack:firefox" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.6", + "@rrweb/types": "^2.0.0-alpha.7", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^2.1.0", @@ -40,7 +40,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.6", - "rrweb-player": "^2.0.0-alpha.6" + "rrweb": "^2.0.0-alpha.7", + "rrweb-player": "^2.0.0-alpha.7" } } From 351c5551da748a261a42723960c2bcfe01774c0a Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Mon, 13 Feb 2023 17:27:15 +0000 Subject: [PATCH 163/201] Record pointerType on clicks - could be useful for displaying e.g. a circle rather than a point during replay - We have to switch to 'onpointerdown' & 'onpointerup' in order to actually capture `e.pointerType` - this replaces 4 event listeners (MouseDown/MouseUp/TouchStart/TouchEnd) with 2 pointer ones which should fire in all 4 scenarios. We still output the old types according to the MouseInteractions enum - there is no Pointer equivalent of Click, so we leave that is, but use the last Pointer event to attach a pointerType to (only) the click event, where it is most useful - we can fallback to the old method for any browsers not supporting `window.PointerEvent`, in which case \`pointerType\` will be absent from all events --- packages/rrweb/src/record/observer.ts | 49 ++++++++++++++++++++++++--- packages/rrweb/src/utils.ts | 4 +-- packages/types/src/index.ts | 1 + 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index d2852b970a..01a23218be 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -9,7 +9,7 @@ import { getWindowHeight, getWindowWidth, isBlocked, - isTouchEvent, + legacy_isTouchEvent, patch, StyleSheetMirror, } from '../utils'; @@ -170,7 +170,8 @@ function initMoveObserver({ throttle( callbackWrapper((evt) => { const target = getEventTarget(evt); - const { clientX, clientY } = isTouchEvent(evt) + // 'legacy' here as we could switch to https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event + const { clientX, clientY } = legacy_isTouchEvent(evt) ? evt.changedTouches[0] : evt; if (!timeBaseline) { @@ -228,13 +229,38 @@ function initMouseInteractionObserver({ : sampling.mouseInteraction; const handlers: listenerHandler[] = []; + let currentPointerType = null; const getHandler = (eventKey: keyof typeof MouseInteractions) => { - return (event: MouseEvent | TouchEvent) => { + return (event: MouseEvent | TouchEvent | PointerEvent) => { const target = getEventTarget(event) as Node; if (isBlocked(target, blockClass, blockSelector, true)) { return; } - const e = isTouchEvent(event) ? event.changedTouches[0] : event; + let pointerType = null; + let e = event; + if ('pointerType' in e) { + pointerType = (e as PointerEvent).pointerType; // touch / pen / mouse + if (pointerType === 'touch') { + if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) { + // we are actually listening on 'pointerdown' + eventKey = 'TouchStart'; + } else if (MouseInteractions[eventKey] === MouseInteractions.MouseUp) { + // we are actually listening on 'pointerup' + eventKey = 'TouchEnd'; + } + } else if (pointerType == 'pen') { + // TODO: these will get incorrectly emitted as MouseDown/MouseUp + } + } else if (legacy_isTouchEvent(event)) { + e = event.changedTouches[0]; + pointerType = 'touch'; + } + if (pointerType) { + currentPointerType = pointerType; + } else if (MouseInteractions[eventKey] === MouseInteractions.Click) { + pointerType = currentPointerType; + currentPointerType = null; // cleanup as we've used it + } if (!e) { return; } @@ -245,6 +271,7 @@ function initMouseInteractionObserver({ id, x: clientX, y: clientY, + ...pointerType && { pointerType } }); }; }; @@ -256,8 +283,20 @@ function initMouseInteractionObserver({ disableMap[key] !== false, ) .forEach((eventKey: keyof typeof MouseInteractions) => { - const eventName = eventKey.toLowerCase(); + let eventName = eventKey.toLowerCase(); const handler = getHandler(eventKey); + if (window.PointerEvent) { + switch(MouseInteractions[eventKey]) { + case MouseInteractions.MouseDown: + case MouseInteractions.MouseUp: + eventName = eventName.replace('mouse', 'pointer'); + break; + case MouseInteractions.TouchStart: + case MouseInteractions.TouchEnd: + // these are handled by pointerdown/pointerup + return; + } + } handlers.push(on(eventName, handler, doc)); }); return callbackWrapper(() => { diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 1626e3734c..78ca08448d 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -277,8 +277,8 @@ export function isAncestorRemoved(target: Node, mirror: Mirror): boolean { return isAncestorRemoved(target.parentNode, mirror); } -export function isTouchEvent( - event: MouseEvent | TouchEvent, +export function legacy_isTouchEvent( + event: MouseEvent | TouchEvent | PointerEvent, ): event is TouchEvent { return Boolean((event as TouchEvent).changedTouches); } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 1188ef2fb7..2c04794829 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -404,6 +404,7 @@ type mouseInteractionParam = { id: number; x: number; y: number; + pointerType?: string; }; export type mouseInteractionCallBack = (d: mouseInteractionParam) => void; From 73ee29f2082169b74d9fa77f111c0799d140ac26 Mon Sep 17 00:00:00 2001 From: eoghanmurray Date: Thu, 30 Mar 2023 16:12:33 +0000 Subject: [PATCH 164/201] Apply formatting changes --- packages/rrweb/src/record/observer.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 01a23218be..bdfaae546f 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -244,7 +244,9 @@ function initMouseInteractionObserver({ if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) { // we are actually listening on 'pointerdown' eventKey = 'TouchStart'; - } else if (MouseInteractions[eventKey] === MouseInteractions.MouseUp) { + } else if ( + MouseInteractions[eventKey] === MouseInteractions.MouseUp + ) { // we are actually listening on 'pointerup' eventKey = 'TouchEnd'; } @@ -259,7 +261,7 @@ function initMouseInteractionObserver({ currentPointerType = pointerType; } else if (MouseInteractions[eventKey] === MouseInteractions.Click) { pointerType = currentPointerType; - currentPointerType = null; // cleanup as we've used it + currentPointerType = null; // cleanup as we've used it } if (!e) { return; @@ -271,7 +273,7 @@ function initMouseInteractionObserver({ id, x: clientX, y: clientY, - ...pointerType && { pointerType } + ...(pointerType && { pointerType }), }); }; }; @@ -286,15 +288,15 @@ function initMouseInteractionObserver({ let eventName = eventKey.toLowerCase(); const handler = getHandler(eventKey); if (window.PointerEvent) { - switch(MouseInteractions[eventKey]) { - case MouseInteractions.MouseDown: - case MouseInteractions.MouseUp: - eventName = eventName.replace('mouse', 'pointer'); - break; - case MouseInteractions.TouchStart: - case MouseInteractions.TouchEnd: - // these are handled by pointerdown/pointerup - return; + switch (MouseInteractions[eventKey]) { + case MouseInteractions.MouseDown: + case MouseInteractions.MouseUp: + eventName = eventName.replace('mouse', 'pointer'); + break; + case MouseInteractions.TouchStart: + case MouseInteractions.TouchEnd: + // these are handled by pointerdown/pointerup + return; } } handlers.push(on(eventName, handler, doc)); From 979d2b1847a3d05e2731722952e4d6bd8be54f40 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Thu, 30 Mar 2023 17:22:16 +0100 Subject: [PATCH 165/201] Create little-suits-leave.md --- .changeset/little-suits-leave.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/little-suits-leave.md diff --git a/.changeset/little-suits-leave.md b/.changeset/little-suits-leave.md new file mode 100644 index 0000000000..3972ff48c0 --- /dev/null +++ b/.changeset/little-suits-leave.md @@ -0,0 +1,6 @@ +--- +"rrweb": minor +"@rrweb/types": minor +--- + +click events (as well as mousedown/mouseup/touchstart/touchend events) now include a `.pointerType` attribute which distinguishes between ['pen', 'mouse' and 'touch' events](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType) From 2114dc47d478b4fa892ac1f87f8eee36680504dd Mon Sep 17 00:00:00 2001 From: eoghanmurray Date: Thu, 30 Mar 2023 16:23:41 +0000 Subject: [PATCH 166/201] Apply formatting changes --- .changeset/little-suits-leave.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/little-suits-leave.md b/.changeset/little-suits-leave.md index 3972ff48c0..29f44bb5ff 100644 --- a/.changeset/little-suits-leave.md +++ b/.changeset/little-suits-leave.md @@ -1,6 +1,6 @@ --- -"rrweb": minor -"@rrweb/types": minor +'rrweb': minor +'@rrweb/types': minor --- click events (as well as mousedown/mouseup/touchstart/touchend events) now include a `.pointerType` attribute which distinguishes between ['pen', 'mouse' and 'touch' events](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType) From d7beb11ca3c956dff06cc5ca4df519f920c4a4fa Mon Sep 17 00:00:00 2001 From: xujiujiu <906784584@qq.com> Date: Sun, 2 Apr 2023 19:40:01 +0800 Subject: [PATCH 167/201] fix: doc fix canvas-webrtc link (#1193) --- docs/recipes/canvas.zh_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/recipes/canvas.zh_CN.md b/docs/recipes/canvas.zh_CN.md index 770864601c..1f522c9bcf 100644 --- a/docs/recipes/canvas.zh_CN.md +++ b/docs/recipes/canvas.zh_CN.md @@ -41,4 +41,4 @@ replayer.play(); **回放 Canvas 将会关闭沙盒策略,导致一定风险**。 另外,您可以使用 canvas-webrtc 插件通过 WEBRTC 流式传输 Canvas 元素。 -有关更多信息,请参考[canvas-webrtc 文档](../../packages/rrweb/src/plugins/canvas-webrtc/readme.md) +有关更多信息,请参考[canvas-webrtc 文档](../../packages/rrweb/src/plugins/canvas-webrtc/Readme.md) From 3a6de4b1292cb62f8ead3856ae30fb295029789e Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 5 Apr 2023 15:06:46 +0100 Subject: [PATCH 168/201] Make the pointerType into an Enum to be consistent with other values in events --- packages/rrweb/src/record/observer.ts | 21 ++++++++++++++------- packages/types/src/index.ts | 8 +++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index bdfaae546f..241c0e5645 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -20,6 +20,7 @@ import { mousePosition, mouseInteractionCallBack, MouseInteractions, + PointerTypes, listenerHandler, scrollCallback, styleSheetRuleCallback, @@ -236,11 +237,17 @@ function initMouseInteractionObserver({ if (isBlocked(target, blockClass, blockSelector, true)) { return; } - let pointerType = null; + let pointerType: PointerTypes | null = null; let e = event; if ('pointerType' in e) { - pointerType = (e as PointerEvent).pointerType; // touch / pen / mouse - if (pointerType === 'touch') { + Object.keys(PointerTypes) + .forEach((pointerKey: keyof typeof PointerKeys) => { + if ((e as PointerEvent).pointerType === pointerKey.toLowerCase()) { + pointerType = PointerTypes[pointerKey]; + return; + } + }); + if (pointerType === PointerTypes.Touch) { if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) { // we are actually listening on 'pointerdown' eventKey = 'TouchStart'; @@ -250,14 +257,14 @@ function initMouseInteractionObserver({ // we are actually listening on 'pointerup' eventKey = 'TouchEnd'; } - } else if (pointerType == 'pen') { + } else if (pointerType == PointerTypes.Pen) { // TODO: these will get incorrectly emitted as MouseDown/MouseUp } } else if (legacy_isTouchEvent(event)) { e = event.changedTouches[0]; - pointerType = 'touch'; + pointerType = PointerTypes.Touch; } - if (pointerType) { + if (pointerType !== null) { currentPointerType = pointerType; } else if (MouseInteractions[eventKey] === MouseInteractions.Click) { pointerType = currentPointerType; @@ -273,7 +280,7 @@ function initMouseInteractionObserver({ id, x: clientX, y: clientY, - ...(pointerType && { pointerType }), + ...(pointerType !== null && { pointerType }), }); }; }; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2c04794829..6601457291 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -362,6 +362,12 @@ export enum MouseInteractions { TouchCancel, } +export enum PointerTypes { + Mouse, + Pen, + Touch, +} + export enum CanvasContext { '2D', WebGL, @@ -404,7 +410,7 @@ type mouseInteractionParam = { id: number; x: number; y: number; - pointerType?: string; + pointerType?: PointerTypes; }; export type mouseInteractionCallBack = (d: mouseInteractionParam) => void; From be856443dd66eb9068637858dc75851208b02e2d Mon Sep 17 00:00:00 2001 From: eoghanmurray Date: Wed, 5 Apr 2023 15:51:24 +0000 Subject: [PATCH 169/201] Apply formatting changes --- packages/rrweb/src/record/observer.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 241c0e5645..2e4ab35976 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -240,13 +240,14 @@ function initMouseInteractionObserver({ let pointerType: PointerTypes | null = null; let e = event; if ('pointerType' in e) { - Object.keys(PointerTypes) - .forEach((pointerKey: keyof typeof PointerKeys) => { + Object.keys(PointerTypes).forEach( + (pointerKey: keyof typeof PointerKeys) => { if ((e as PointerEvent).pointerType === pointerKey.toLowerCase()) { pointerType = PointerTypes[pointerKey]; return; } - }); + }, + ); if (pointerType === PointerTypes.Touch) { if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) { // we are actually listening on 'pointerdown' From d0fdc0f273bb156a1faab4782b40fbec8dccf915 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 7 Apr 2023 19:25:22 +1000 Subject: [PATCH 170/201] improve: add try catch to snapshot.ts 's masking text function (#1148) * improve: add try catch to snapshot.ts to make it robust * add change log --- .changeset/pretty-schools-remember.md | 5 +++ packages/rrweb-snapshot/src/snapshot.ts | 60 ++++++++++++++----------- 2 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 .changeset/pretty-schools-remember.md diff --git a/.changeset/pretty-schools-remember.md b/.changeset/pretty-schools-remember.md new file mode 100644 index 0000000000..920125d15e --- /dev/null +++ b/.changeset/pretty-schools-remember.md @@ -0,0 +1,5 @@ +--- +'rrweb-snapshot': patch +--- + +Improve: Add try catch to snapshot.ts 's masking text function. Fixes [#1118](https://github.com/rrweb-io/rrweb/issues/1118). diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index f587bd83ed..8c3fda1a2e 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -268,20 +268,24 @@ export function _isBlockedElement( blockClass: string | RegExp, blockSelector: string | null, ): boolean { - if (typeof blockClass === 'string') { - if (element.classList.contains(blockClass)) { - return true; - } - } else { - for (let eIndex = element.classList.length; eIndex--; ) { - const className = element.classList[eIndex]; - if (blockClass.test(className)) { + try { + if (typeof blockClass === 'string') { + if (element.classList.contains(blockClass)) { return true; } + } else { + for (let eIndex = element.classList.length; eIndex--; ) { + const className = element.classList[eIndex]; + if (blockClass.test(className)) { + return true; + } + } } - } - if (blockSelector) { - return element.matches(blockSelector); + if (blockSelector) { + return element.matches(blockSelector); + } + } catch (e) { + // } return false; @@ -313,22 +317,26 @@ export function needMaskingText( maskTextClass: string | RegExp, maskTextSelector: string | null, ): boolean { - const el: HTMLElement | null = - node.nodeType === node.ELEMENT_NODE - ? (node as HTMLElement) - : node.parentElement; - if (el === null) return false; - - if (typeof maskTextClass === 'string') { - if (el.classList.contains(maskTextClass)) return true; - if (el.closest(`.${maskTextClass}`)) return true; - } else { - if (classMatchesRegex(el, maskTextClass, true)) return true; - } + try { + const el: HTMLElement | null = + node.nodeType === node.ELEMENT_NODE + ? (node as HTMLElement) + : node.parentElement; + if (el === null) return false; + + if (typeof maskTextClass === 'string') { + if (el.classList.contains(maskTextClass)) return true; + if (el.closest(`.${maskTextClass}`)) return true; + } else { + if (classMatchesRegex(el, maskTextClass, true)) return true; + } - if (maskTextSelector) { - if (el.matches(maskTextSelector)) return true; - if (el.closest(maskTextSelector)) return true; + if (maskTextSelector) { + if (el.matches(maskTextSelector)) return true; + if (el.closest(maskTextSelector)) return true; + } + } catch (e) { + // } return false; } From aa79db7568578ea3a413292450cd64f07481e5dd Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 7 Apr 2023 16:18:22 +0200 Subject: [PATCH 171/201] fix: Fix input.type check (#1184) * fix: Fix input.type check Actually I noticed that `el.type` returns `text` when type is not explicitly set, so this is slightly incorrect. * fix linting * Apply formatting changes --- .changeset/nice-pugs-reply.md | 5 + packages/rrweb-snapshot/src/snapshot.ts | 7 +- packages/rrweb-snapshot/src/utils.ts | 17 ++ packages/rrweb/src/record/mutation.ts | 2 +- packages/rrweb/src/record/observer.ts | 8 +- packages/rrweb/src/utils.ts | 15 -- .../__snapshots__/integration.test.ts.snap | 232 ++++++++++++++++++ packages/rrweb/test/html/empty.html | 11 + packages/rrweb/test/integration.test.ts | 24 ++ 9 files changed, 298 insertions(+), 23 deletions(-) create mode 100644 .changeset/nice-pugs-reply.md create mode 100644 packages/rrweb/test/html/empty.html diff --git a/.changeset/nice-pugs-reply.md b/.changeset/nice-pugs-reply.md new file mode 100644 index 0000000000..04c655a67e --- /dev/null +++ b/.changeset/nice-pugs-reply.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +fix: Ensure getting the type of inputs works diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 8c3fda1a2e..364506fd6a 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -20,6 +20,7 @@ import { maskInputValue, isNativeShadowDom, getCssRulesString, + getInputType, } from './utils'; let _id = 1; @@ -682,11 +683,7 @@ function serializeElementNode( attributes.type !== 'button' && value ) { - const type: string | null = n.hasAttribute('data-rr-is-password') - ? 'password' - : typeof attributes.type === 'string' - ? attributes.type.toLowerCase() - : null; + const type = getInputType(n); attributes.value = maskInputValue({ type, tagName, diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 35b6d8075e..a443cb96c1 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -248,3 +248,20 @@ export function isNodeMetaEqual(a: serializedNode, b: serializedNode): boolean { ); return false; } + +/** + * Get the type of an input element. + * This takes care of the case where a password input is changed to a text input. + * In this case, we continue to consider this of type password, in order to avoid leaking sensitive data + * where passwords should be masked. + */ +export function getInputType(element: HTMLElement): Lowercase | null { + const type = (element as HTMLInputElement).type; + + return element.hasAttribute('data-rr-is-password') + ? 'password' + : type + ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + (type.toLowerCase() as Lowercase) + : null; +} diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index aa351fee62..ad97f79915 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -8,6 +8,7 @@ import { maskInputValue, Mirror, isNativeShadowDom, + getInputType, } from 'rrweb-snapshot'; import type { observerParam, MutationBufferParam } from '../types'; import type { @@ -29,7 +30,6 @@ import { isSerializedStylesheet, inDom, getShadowHost, - getInputType, } from '../utils'; type DoubleLinkedListNode = { diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index d2852b970a..bb04dd0300 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -1,10 +1,14 @@ -import { MaskInputOptions, maskInputValue, Mirror } from 'rrweb-snapshot'; +import { + MaskInputOptions, + maskInputValue, + Mirror, + getInputType, +} from 'rrweb-snapshot'; import type { FontFaceSet } from 'css-font-loading-module'; import { throttle, on, hookSetter, - getInputType, getWindowScroll, getWindowHeight, getWindowWidth, diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 1626e3734c..26dc63888e 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -563,18 +563,3 @@ export function inDom(n: Node): boolean { if (!doc) return false; return doc.contains(n) || shadowHostInDom(n); } - -/** - * Get the type of an input element. - * This takes care of the case where a password input is changed to a text input. - * In this case, we continue to consider this of type password, in order to avoid leaking sensitive data - * where passwords should be masked. - */ -export function getInputType(element: HTMLElement): Lowercase | null { - return element.hasAttribute('data-rr-is-password') - ? 'password' - : element.hasAttribute('type') - ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion - (element.getAttribute('type')!.toLowerCase() as Lowercase) - : null; -} diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 7c6fdfbfda..32b56a6019 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -7473,6 +7473,238 @@ exports[`record integration tests should not record input events on ignored elem ]" `; +exports[`record integration tests should not record input values if dynamically added and maskAllInputs is true 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Empty\\", + \\"id\\": 11 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 12 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"div\\", + \\"attributes\\": { + \\"id\\": \\"one\\" + }, + \\"childNodes\\": [], + \\"id\\": 16 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 20 + } + ], + \\"id\\": 14 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 14, + \\"nextId\\": 16, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"id\\": \\"input\\", + \\"value\\": \\"**********************\\" + }, + \\"childNodes\\": [], + \\"id\\": 21 + } + } + ] + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**********************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***********************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"************************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*************************\\", + \\"isChecked\\": false, + \\"id\\": 21 + } + } +]" +`; + exports[`record integration tests should not record input values if maskAllInputs is enabled 1`] = ` "[ { diff --git a/packages/rrweb/test/html/empty.html b/packages/rrweb/test/html/empty.html new file mode 100644 index 0000000000..6ceadbba12 --- /dev/null +++ b/packages/rrweb/test/html/empty.html @@ -0,0 +1,11 @@ + + + + + + Empty + + +
+ + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index fec8c36902..aa564d3177 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -455,6 +455,30 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('should not record input values if dynamically added and maskAllInputs is true', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent( + getHtml.call(this, 'empty.html', { maskAllInputs: true }), + ); + + await page.evaluate(() => { + const el = document.createElement('input'); + el.id = 'input'; + el.value = 'input should be masked'; + + const nextElement = document.querySelector('#one')!; + nextElement.parentNode!.insertBefore(el, nextElement); + }); + + await page.type('#input', 'moo'); + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + it('should record webgl canvas mutations', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From b5e30cf6cc7f5335d674ef1917a92bdf2895fe9e Mon Sep 17 00:00:00 2001 From: Charlie Gracie Date: Fri, 7 Apr 2023 21:50:25 -0400 Subject: [PATCH 172/201] fix: Set finished=false in goto instead of handleProgressClick (#1198) * Reset the finished flag in goto If the player finishes a replaying a video the `finished` boolean is set to true. This allows the video to be restarted at the beginning if the controller is toggled again. If a user clicks on the progress bar the `finished` boolean is reset so if the `toggle` API is called it starts at the right place. If a user programatically calls `goto` which is the underlying API that handling the progress click calls the next invocation of `toggle` will start the video at the beginning instead of the proper location. * Create controller-finish-flag.md * Update controller-finish-flag.md --- .changeset/controller-finish-flag.md | 6 ++++++ packages/rrweb-player/src/Controller.svelte | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/controller-finish-flag.md diff --git a/.changeset/controller-finish-flag.md b/.changeset/controller-finish-flag.md new file mode 100644 index 0000000000..567e5db44e --- /dev/null +++ b/.changeset/controller-finish-flag.md @@ -0,0 +1,6 @@ +--- +'rrweb-player': patch +'rrweb': patch +--- + +Reset the finished flag in Controller `goto` instead of `handleProgressClick` so that it is properly handled if `goto` is called directly. diff --git a/packages/rrweb-player/src/Controller.svelte b/packages/rrweb-player/src/Controller.svelte index 57d323133e..5a352e6dd0 100644 --- a/packages/rrweb-player/src/Controller.svelte +++ b/packages/rrweb-player/src/Controller.svelte @@ -208,6 +208,7 @@ export const goto = (timeOffset: number, play?: boolean) => { currentTime = timeOffset; pauseAt = false; + finished = false; const resumePlaying = typeof play === 'boolean' ? play : playerState === 'playing'; if (resumePlaying) { @@ -250,7 +251,6 @@ percent = 1; } const timeOffset = meta.totalTime * percent; - finished = false; goto(timeOffset); }; From b00798099a131b380ad805040046e861a3e8419c Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 12 Apr 2023 12:10:04 +0100 Subject: [PATCH 173/201] Fixup type errors --- packages/rrweb/src/record/observer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 2e4ab35976..31f77298b4 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -230,7 +230,7 @@ function initMouseInteractionObserver({ : sampling.mouseInteraction; const handlers: listenerHandler[] = []; - let currentPointerType = null; + let currentPointerType: PointerTypes | null = null; const getHandler = (eventKey: keyof typeof MouseInteractions) => { return (event: MouseEvent | TouchEvent | PointerEvent) => { const target = getEventTarget(event) as Node; @@ -241,7 +241,7 @@ function initMouseInteractionObserver({ let e = event; if ('pointerType' in e) { Object.keys(PointerTypes).forEach( - (pointerKey: keyof typeof PointerKeys) => { + (pointerKey: keyof typeof PointerTypes) => { if ((e as PointerEvent).pointerType === pointerKey.toLowerCase()) { pointerType = PointerTypes[pointerKey]; return; From 0228137f51000458038e182a744dbd7b4b0c4880 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 12 Apr 2023 12:16:28 +0100 Subject: [PATCH 174/201] The type system was complaining about the remote possibility that `e` ended up as a TouchEvent instead of an individual Touch object I think --- packages/rrweb/src/record/observer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 31f77298b4..6771cf2acb 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -238,11 +238,12 @@ function initMouseInteractionObserver({ return; } let pointerType: PointerTypes | null = null; - let e = event; - if ('pointerType' in e) { + if ('pointerType' in event) { Object.keys(PointerTypes).forEach( (pointerKey: keyof typeof PointerTypes) => { - if ((e as PointerEvent).pointerType === pointerKey.toLowerCase()) { + if ( + (event as PointerEvent).pointerType === pointerKey.toLowerCase() + ) { pointerType = PointerTypes[pointerKey]; return; } @@ -262,7 +263,6 @@ function initMouseInteractionObserver({ // TODO: these will get incorrectly emitted as MouseDown/MouseUp } } else if (legacy_isTouchEvent(event)) { - e = event.changedTouches[0]; pointerType = PointerTypes.Touch; } if (pointerType !== null) { @@ -271,6 +271,7 @@ function initMouseInteractionObserver({ pointerType = currentPointerType; currentPointerType = null; // cleanup as we've used it } + const e = legacy_isTouchEvent(event) ? event.changedTouches[0] : event; if (!e) { return; } From 777ab9a7cd134d0cceb7295bb2089c97c83c17cf Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 12 Apr 2023 12:53:06 +0100 Subject: [PATCH 175/201] `pointerType` shows up in clicks in these tests on my machine; `0=PointerTypes.Mouse` is expected --- .../__snapshots__/integration.test.ts.snap | 123 ++++++++++++------ .../cross-origin-iframes.test.ts.snap | 18 ++- 2 files changed, 94 insertions(+), 47 deletions(-) diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 7c6fdfbfda..9fe06944e8 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1847,7 +1847,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -1871,7 +1872,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -1879,7 +1881,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -1905,7 +1908,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -1929,7 +1933,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -1937,7 +1942,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -2621,7 +2627,8 @@ exports[`record integration tests can record node mutations 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 26 + \\"id\\": 26, + \\"pointerType\\": 0 } }, { @@ -3078,7 +3085,8 @@ exports[`record integration tests can record node mutations 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 70 + \\"id\\": 70, + \\"pointerType\\": 0 } }, { @@ -3625,7 +3633,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -3649,7 +3658,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -3657,7 +3667,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -3683,7 +3694,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -3707,7 +3719,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -3715,7 +3728,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -4670,7 +4684,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 42 + \\"id\\": 42, + \\"pointerType\\": 0 } }, { @@ -4686,7 +4701,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 42 + \\"id\\": 42, + \\"pointerType\\": 0 } }, { @@ -4694,7 +4710,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 42 + \\"id\\": 42, + \\"pointerType\\": 0 } }, { @@ -4793,7 +4810,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 68 + \\"id\\": 68, + \\"pointerType\\": 0 } }, { @@ -4817,7 +4835,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 68 + \\"id\\": 68, + \\"pointerType\\": 0 } }, { @@ -4825,7 +4844,8 @@ exports[`record integration tests mutations should work when blocked class is un \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 68 + \\"id\\": 68, + \\"pointerType\\": 0 } }, { @@ -5384,7 +5404,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -5408,7 +5429,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -5416,7 +5438,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -5492,7 +5515,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -5516,7 +5540,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -5524,7 +5549,8 @@ exports[`record integration tests should mask password value attribute with mask \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 20 + \\"id\\": 20, + \\"pointerType\\": 0 } }, { @@ -7991,7 +8017,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -8015,7 +8042,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -8023,7 +8051,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -8049,7 +8078,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -8073,7 +8103,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -8081,7 +8112,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -10022,7 +10054,8 @@ exports[`record integration tests should record dynamic CSS changes 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 26 + \\"id\\": 26, + \\"pointerType\\": 0 } }, { @@ -10030,7 +10063,8 @@ exports[`record integration tests should record dynamic CSS changes 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 26 + \\"id\\": 26, + \\"pointerType\\": 0 } }, { @@ -10038,7 +10072,8 @@ exports[`record integration tests should record dynamic CSS changes 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 26 + \\"id\\": 26, + \\"pointerType\\": 0 } }, { @@ -11700,7 +11735,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -11724,7 +11760,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -11732,7 +11769,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 27 + \\"id\\": 27, + \\"pointerType\\": 0 } }, { @@ -11760,7 +11798,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -11784,7 +11823,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { @@ -11792,7 +11832,8 @@ exports[`record integration tests should record input userTriggered values if us \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 37 + \\"id\\": 37, + \\"pointerType\\": 0 } }, { diff --git a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap index 30178f6c64..204e2dcf32 100644 --- a/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap +++ b/packages/rrweb/test/record/__snapshots__/cross-origin-iframes.test.ts.snap @@ -1667,7 +1667,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 39 + \\"id\\": 39, + \\"pointerType\\": 0 } }, { @@ -1691,7 +1692,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 39 + \\"id\\": 39, + \\"pointerType\\": 0 } }, { @@ -1699,7 +1701,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 39 + \\"id\\": 39, + \\"pointerType\\": 0 } }, { @@ -1725,7 +1728,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 1, - \\"id\\": 49 + \\"id\\": 49, + \\"pointerType\\": 0 } }, { @@ -1749,7 +1753,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 49 + \\"id\\": 49, + \\"pointerType\\": 0 } }, { @@ -1757,7 +1762,8 @@ exports[`cross origin iframes form.html should map input events correctly 1`] = \\"data\\": { \\"source\\": 2, \\"type\\": 2, - \\"id\\": 49 + \\"id\\": 49, + \\"pointerType\\": 0 } }, { From 3ce6903122b039f9fb200c9a6130321772cac117 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Mon, 9 Aug 2021 12:56:42 +0100 Subject: [PATCH 176/201] Add a baseline test for clicking links --- .../__snapshots__/integration.test.ts.snap | 261 ++++++++++++++++++ packages/rrweb/test/html/link.html | 14 + packages/rrweb/test/integration.test.ts | 11 + 3 files changed, 286 insertions(+) create mode 100644 packages/rrweb/test/html/link.html diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 9fe06944e8..eae0bfaf97 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1328,6 +1328,267 @@ exports[`record integration tests can record childList mutations 1`] = ` ]" `; +exports[`record integration tests can record clicks 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"Link click\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"span\\", + \\"attributes\\": { + \\"id\\": \\"not-a-link\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"not link\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 20 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"a\\", + \\"attributes\\": { + \\"href\\": \\"about:blank#clicked\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"link\\\\n \\\\n \\", + \\"id\\": 22 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 24 + } + ], + \\"id\\": 23 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 25 + } + ], + \\"id\\": 21 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 18, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 18, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 18, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 21, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 21 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 21, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 21, + \\"pointerType\\": 0 + } + } +]" +`; + exports[`record integration tests can record form interactions 1`] = ` "[ { diff --git a/packages/rrweb/test/html/link.html b/packages/rrweb/test/html/link.html new file mode 100644 index 0000000000..0d7b13739d --- /dev/null +++ b/packages/rrweb/test/html/link.html @@ -0,0 +1,14 @@ + + + + + + + Link click + + + + not link +
link + + diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index fec8c36902..f12968e254 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -62,6 +62,17 @@ describe('record integration tests', function (this: ISuite) { server.close(); }); + it('can record clicks', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto('about:blank'); + await page.setContent(getHtml.call(this, 'link.html')); + await page.click('span'); + await page.click('a'); + + const snapshots = await page.evaluate('window.snapshots'); + assertSnapshot(snapshots); + }); + it('can record form interactions', async () => { const page: puppeteer.Page = await browser.newPage(); await page.goto('about:blank'); From 961abec782a42ad9ea6156d06e3fd1cd4afa5f23 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 12 Apr 2023 14:22:19 +0100 Subject: [PATCH 177/201] Get puppeteer to produce a touch event - this also uncovered an error where a subsequent click would be picked up as a touch (see `thisEventKey` change) --- packages/rrweb/src/record/observer.ts | 7 ++--- .../__snapshots__/integration.test.ts.snap | 27 +++++++++++++++++++ packages/rrweb/test/integration.test.ts | 12 +++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 6771cf2acb..810c137f84 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -238,6 +238,7 @@ function initMouseInteractionObserver({ return; } let pointerType: PointerTypes | null = null; + let thisEventKey = eventKey; if ('pointerType' in event) { Object.keys(PointerTypes).forEach( (pointerKey: keyof typeof PointerTypes) => { @@ -252,12 +253,12 @@ function initMouseInteractionObserver({ if (pointerType === PointerTypes.Touch) { if (MouseInteractions[eventKey] === MouseInteractions.MouseDown) { // we are actually listening on 'pointerdown' - eventKey = 'TouchStart'; + thisEventKey = 'TouchStart'; } else if ( MouseInteractions[eventKey] === MouseInteractions.MouseUp ) { // we are actually listening on 'pointerup' - eventKey = 'TouchEnd'; + thisEventKey = 'TouchEnd'; } } else if (pointerType == PointerTypes.Pen) { // TODO: these will get incorrectly emitted as MouseDown/MouseUp @@ -278,7 +279,7 @@ function initMouseInteractionObserver({ const id = mirror.getId(target); const { clientX, clientY } = e; callbackWrapper(mouseInteractionCb)({ - type: MouseInteractions[eventKey], + type: MouseInteractions[thisEventKey], id, x: clientX, y: clientY, diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index eae0bfaf97..5ae9007102 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1551,6 +1551,33 @@ exports[`record integration tests can record clicks 1`] = ` \\"pointerType\\": 0 } }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 7, + \\"id\\": 18, + \\"pointerType\\": 2 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 9, + \\"id\\": 18, + \\"pointerType\\": 2 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 18, + \\"pointerType\\": 2 + } + }, { \\"type\\": 3, \\"data\\": { diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index f12968e254..9033761de3 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -67,6 +67,18 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'link.html')); await page.click('span'); + + // also tap on the span + const span = await page.waitForSelector('span'); + const center = await page.evaluate((el) => { + const { x, y, width, height } = el.getBoundingClientRect(); + return { + x: Math.round(x + width / 2), + y: Math.round(y + height / 2), + }; + }, span); + await page.touchscreen.tap(center.x, center.y); + await page.click('a'); const snapshots = await page.evaluate('window.snapshots'); From 004d29ba70cef4ce88db7a65181cdcabcb3eaa81 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Wed, 12 Apr 2023 15:08:13 +0100 Subject: [PATCH 178/201] eslint was giving an error here --- packages/rrweb/src/record/observer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 810c137f84..4753aefb92 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -242,9 +242,7 @@ function initMouseInteractionObserver({ if ('pointerType' in event) { Object.keys(PointerTypes).forEach( (pointerKey: keyof typeof PointerTypes) => { - if ( - (event as PointerEvent).pointerType === pointerKey.toLowerCase() - ) { + if (event.pointerType === pointerKey.toLowerCase()) { pointerType = PointerTypes[pointerKey]; return; } From 4939c0000443fea84612bc8467d8d3870fe10d78 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Wed, 12 Apr 2023 23:07:16 +0200 Subject: [PATCH 179/201] Create fair-dragons-greet.md --- .changeset/fair-dragons-greet.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/fair-dragons-greet.md diff --git a/.changeset/fair-dragons-greet.md b/.changeset/fair-dragons-greet.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/fair-dragons-greet.md @@ -0,0 +1,2 @@ +--- +--- From 94d06536e35cee6f5e10aebdea402ff994c76197 Mon Sep 17 00:00:00 2001 From: re-fort Date: Fri, 14 Apr 2023 17:04:02 +0900 Subject: [PATCH 180/201] fix: change default value of input type from null to text (#1200) * fix: change default value of input type from null to text * Apply formatting changes * add changeset * add a comment related to the type of input element --- packages/rrweb-snapshot/src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index a443cb96c1..110cf1a735 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -256,6 +256,7 @@ export function isNodeMetaEqual(a: serializedNode, b: serializedNode): boolean { * where passwords should be masked. */ export function getInputType(element: HTMLElement): Lowercase | null { + // when omitting the type of input element(e.g. ), the type is treated as text const type = (element as HTMLInputElement).type; return element.hasAttribute('data-rr-is-password') From 944615d99949cdeff5ab75d9ae00dd84c0b27426 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sat, 15 Apr 2023 01:16:45 +0200 Subject: [PATCH 181/201] Add gold sponsors (#1207) * Add gold sponsors * Create empty-bikes-cheer.md --- .changeset/empty-bikes-cheer.md | 2 ++ README.md | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .changeset/empty-bikes-cheer.md diff --git a/.changeset/empty-bikes-cheer.md b/.changeset/empty-bikes-cheer.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/empty-bikes-cheer.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/README.md b/README.md index 7e81e98957..c50f5d56df 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,47 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE [Using the REPL tool](./guide.md#REPL-tool) +## Sponsors + +### Gold + +[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ## Core Team Members From bc84246f78849a80dbb8fe9b4e76117afcc5c3f7 Mon Sep 17 00:00:00 2001 From: Ben White Date: Sat, 15 Apr 2023 09:31:39 +0200 Subject: [PATCH 182/201] feat: Added support maskInputFn with HTMLElement (#1188) --- .changeset/nervous-tables-travel.md | 6 + packages/rrweb-snapshot/src/snapshot.ts | 1 + packages/rrweb-snapshot/src/types.ts | 2 +- packages/rrweb-snapshot/src/utils.ts | 4 +- packages/rrweb/src/record/mutation.ts | 1 + packages/rrweb/src/record/observer.ts | 1 + .../__snapshots__/integration.test.ts.snap | 872 +++++++++++++++++- packages/rrweb/test/html/form.html | 2 +- packages/rrweb/test/integration.test.ts | 30 + .../cross-origin-iframes.test.ts.snap | 6 +- packages/rrweb/test/utils.ts | 1 + 11 files changed, 917 insertions(+), 9 deletions(-) create mode 100644 .changeset/nervous-tables-travel.md diff --git a/.changeset/nervous-tables-travel.md b/.changeset/nervous-tables-travel.md new file mode 100644 index 0000000000..d190cbe89f --- /dev/null +++ b/.changeset/nervous-tables-travel.md @@ -0,0 +1,6 @@ +--- +'rrweb-snapshot': minor +'rrweb': minor +--- + +feat: Extends maskInputFn to pass the HTMLElement to the deciding function diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 364506fd6a..0eeb289b4f 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -685,6 +685,7 @@ function serializeElementNode( ) { const type = getInputType(n); attributes.value = maskInputValue({ + element: n, type, tagName, value, diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 1666dd4f80..9edb4dd6d4 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -154,7 +154,7 @@ export type DataURLOptions = Partial<{ }>; export type MaskTextFn = (text: string) => string; -export type MaskInputFn = (text: string) => string; +export type MaskInputFn = (text: string, element: HTMLElement) => string; export type KeepIframeSrcFn = (src: string) => boolean; diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 110cf1a735..c2f85e6e08 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -154,12 +154,14 @@ export function createMirror(): Mirror { } export function maskInputValue({ + element, maskInputOptions, tagName, type, value, maskInputFn, }: { + element: HTMLElement; maskInputOptions: MaskInputOptions; tagName: string; type: string | null; @@ -174,7 +176,7 @@ export function maskInputValue({ (actualType && maskInputOptions[actualType as keyof MaskInputOptions]) ) { if (maskInputFn) { - text = maskInputFn(text); + text = maskInputFn(text, element); } else { text = '*'.repeat(text.length); } diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index ad97f79915..2e5132c9ae 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -494,6 +494,7 @@ export default class MutationBuffer { const type = getInputType(target); value = maskInputValue({ + element: target, maskInputOptions: this.maskInputOptions, tagName: target.tagName, type, diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index ff8eac7cc3..5aef5e4747 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -444,6 +444,7 @@ function initInputObserver({ maskInputOptions[type as keyof MaskInputOptions] ) { text = maskInputValue({ + element: target, maskInputOptions, tagName, type, diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index 73a8f472d7..00c98f580e 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1910,7 +1910,8 @@ exports[`record integration tests can record form interactions 1`] = ` \\"name\\": \\"\\", \\"id\\": \\"\\", \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -3696,7 +3697,8 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"name\\": \\"\\", \\"id\\": \\"\\", \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -5410,6 +5412,866 @@ exports[`record integration tests should handle recursive console messages 1`] = ]" `; +exports[`record integration tests should mask inputs via function call 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"form fields\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + } + ], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 26 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 27 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 28 + } + ], + \\"id\\": 25 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + } + ], + \\"id\\": 35 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 39 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" + }, + \\"childNodes\\": [], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + } + ], + \\"id\\": 40 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 44 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"*\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 50 + } + ], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 53 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 54 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 45 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 61 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 62 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 64 + } + ], + \\"id\\": 63 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 65 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 27, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 27, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 27, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"off\\", + \\"isChecked\\": false, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 37, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 37, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 37, + \\"pointerType\\": 0 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 59 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tex\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"text\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"texta\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textar\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textare\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea \\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea t\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea te\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea tes\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"textarea test\\", + \\"isChecked\\": false, + \\"id\\": 42 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 47 + } + } +]" +`; + exports[`record integration tests should mask password value attribute with maskInputOptions 1`] = ` "[ { @@ -8313,7 +9175,8 @@ exports[`record integration tests should not record input values if maskAllInput \\"name\\": \\"\\", \\"id\\": \\"\\", \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" }, \\"childNodes\\": [], \\"id\\": 42 @@ -12026,7 +12889,8 @@ exports[`record integration tests should record input userTriggered values if us \\"name\\": \\"\\", \\"id\\": \\"\\", \\"cols\\": \\"30\\", - \\"rows\\": \\"10\\" + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" }, \\"childNodes\\": [], \\"id\\": 42 diff --git a/packages/rrweb/test/html/form.html b/packages/rrweb/test/html/form.html index a89f11ff74..9125e983fa 100644 --- a/packages/rrweb/test/html/form.html +++ b/packages/rrweb/test/html/form.html @@ -22,7 +22,7 @@
From 1d169d585ac67cbd3367b54d55d0f3d4d33e813c Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Sun, 30 Apr 2023 01:17:19 +0200 Subject: [PATCH 186/201] Add highlight to "who's using rrweb" (#1215) * Add highlight to "who's using rrweb" * Create rich-crews-protect.md --- .changeset/rich-crews-protect.md | 2 ++ README.md | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/rich-crews-protect.md diff --git a/.changeset/rich-crews-protect.md b/.changeset/rich-crews-protect.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/rich-crews-protect.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/README.md b/README.md index 71d0c145d2..bf70a96b6a 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE +
- + Smart screen recording for SaaS @@ -192,5 +192,10 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE Remote Access & Co-Browsing + + The open source, fullstack Monitoring Platform. + +
From ebcbe8b0d746a0a4c07d3530387f920900f35215 Mon Sep 17 00:00:00 2001 From: fukang wang Date: Mon, 1 May 2023 10:15:58 +0800 Subject: [PATCH 187/201] perf: record processMutation (#1214) * perf: record processMutation * Create mean-tips-impress.md * Apply formatting changes --- .changeset/mean-tips-impress.md | 5 +++++ packages/rrweb/src/record/mutation.ts | 3 +++ .../rrweb/test/benchmark/dom-mutation.test.ts | 6 ++++++ ...k-dom-mutation-multiple-descendant-add.html | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 .changeset/mean-tips-impress.md create mode 100644 packages/rrweb/test/html/benchmark-dom-mutation-multiple-descendant-add.html diff --git a/.changeset/mean-tips-impress.md b/.changeset/mean-tips-impress.md new file mode 100644 index 0000000000..b6b0022117 --- /dev/null +++ b/.changeset/mean-tips-impress.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +perf: optimize the performance of record in processMutation phase diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 2e5132c9ae..e0b03eb1dd 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -656,6 +656,9 @@ export default class MutationBuffer { // this node was already recorded in other buffer, ignore it if (this.processedNodeManager.inOtherBuffer(n, this)) return; + // if n is added to set, there is no need to travel it and its' children again + if (this.addedSet.has(n) || this.movedSet.has(n)) return; + if (this.mirror.hasNode(n)) { if (isIgnored(n, this.mirror)) { return; diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts index 47fbfe5e39..57050fb400 100644 --- a/packages/rrweb/test/benchmark/dom-mutation.test.ts +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -30,6 +30,12 @@ const suites: Array< eval: 'window.workload()', times: 10, }, + { + title: 'create 1000 DOM nodes and append into its previous looped node', + html: 'benchmark-dom-mutation-multiple-descendant-add.html', + eval: 'window.workload()', + times: 5, + }, ]; function avg(v: number[]): number { diff --git a/packages/rrweb/test/html/benchmark-dom-mutation-multiple-descendant-add.html b/packages/rrweb/test/html/benchmark-dom-mutation-multiple-descendant-add.html new file mode 100644 index 0000000000..3e727cae59 --- /dev/null +++ b/packages/rrweb/test/html/benchmark-dom-mutation-multiple-descendant-add.html @@ -0,0 +1,18 @@ + + + + From 23d01387f439db68d2874879242b6ade3e103f75 Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Mon, 1 May 2023 22:04:38 +1000 Subject: [PATCH 188/201] rrvideo: improve the video quality and add a progress bar for the CLI tool (#1197) * refactor rrvideo: use playwright rather than puppeteer * add a progress bar for the tool * add tests for cli.ts * fix build error * add change log * update readme file * Apply a scaling method to improve the resolution of the output video --- .changeset/lazy-toes-confess.md | 5 + packages/rrvideo/README.md | 9 +- packages/rrvideo/README.zh_CN.md | 9 +- packages/rrvideo/demo/demo.gif | Bin 0 -> 630758 bytes packages/rrvideo/jest.config.js | 6 + packages/rrvideo/package.json | 12 +- packages/rrvideo/src/cli.ts | 9 + packages/rrvideo/src/index.ts | 273 ++++++++++-------------- packages/rrvideo/test/cli.test.ts | 45 ++++ packages/rrvideo/test/events/example.ts | 147 +++++++++++++ packages/rrvideo/test/tsconfig.json | 3 + packages/rrvideo/tsconfig.json | 1 + packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/package.json | 2 +- packages/rrweb/test/events/input.ts | 1 + yarn.lock | 166 +++++--------- 16 files changed, 414 insertions(+), 276 deletions(-) create mode 100644 .changeset/lazy-toes-confess.md create mode 100644 packages/rrvideo/demo/demo.gif create mode 100644 packages/rrvideo/jest.config.js create mode 100644 packages/rrvideo/test/cli.test.ts create mode 100644 packages/rrvideo/test/events/example.ts create mode 100644 packages/rrvideo/test/tsconfig.json diff --git a/.changeset/lazy-toes-confess.md b/.changeset/lazy-toes-confess.md new file mode 100644 index 0000000000..f3e159a61e --- /dev/null +++ b/.changeset/lazy-toes-confess.md @@ -0,0 +1,5 @@ +--- +'rrvideo': patch +--- + +Refactor: Improve the video quality and add a progress bar for the CLI tool diff --git a/packages/rrvideo/README.md b/packages/rrvideo/README.md index 25d988aa0d..cec3e7d6e9 100644 --- a/packages/rrvideo/README.md +++ b/packages/rrvideo/README.md @@ -4,11 +4,12 @@ rrvideo is a tool for transforming the session recorded by [rrweb](https://github.com/rrweb-io/rrweb) into a video. +![Demo Video](./demo/demo.gif) + ## Install rrvideo -1. Install [ffmpeg](https://ffmpeg.org/download.html)。 -2. Install [Node.JS](https://nodejs.org/en/download/)。 -3. Run `npm i -g rrvideo` to install the rrvideo CLI。 +1. Install [Node.JS](https://nodejs.org/en/download/)。 +2. Run `npm i -g rrvideo` to install the rrvideo CLI. ## Use rrvideo @@ -18,7 +19,7 @@ rrvideo is a tool for transforming the session recorded by [rrweb](https://githu rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE ``` -Running this command will output a `rrvideo-output.mp4` file in the current working directory. +Running this command will output a `rrvideo-output.webm` file in the current working directory. ### Config the output path diff --git a/packages/rrvideo/README.zh_CN.md b/packages/rrvideo/README.zh_CN.md index d133e87335..9a625f1fb6 100644 --- a/packages/rrvideo/README.zh_CN.md +++ b/packages/rrvideo/README.zh_CN.md @@ -2,11 +2,12 @@ rrvideo 是用于将 [rrweb](https://github.com/rrweb-io/rrweb) 录制的数据转为视频格式的工具。 +![Demo Video](./demo/demo.gif) + ## 安装 rrvideo -1. 安装 [ffmpeg](https://ffmpeg.org/download.html)。 -2. 安装 [Node.JS](https://nodejs.org/en/download/)。 -3. 执行 `npm i -g rrvideo` 以安装 rrvideo CLI。 +1. 安装 [Node.JS](https://nodejs.org/en/download/)。 +2. 执行 `npm i -g rrvideo` 以安装 rrvideo CLI。 ## 使用 rrvideo @@ -16,7 +17,7 @@ rrvideo 是用于将 [rrweb](https://github.com/rrweb-io/rrweb) 录制的数据 rrvideo --input PATH_TO_YOUR_RRWEB_EVENTS_FILE ``` -运行以上命令会在执行文件夹中生成一个 `rrvideo-output.mp4` 文件。 +运行以上命令会在执行文件夹中生成一个 `rrvideo-output.webm` 文件。 ### 指定输出路径 diff --git a/packages/rrvideo/demo/demo.gif b/packages/rrvideo/demo/demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..510e4ebe882c58f481aa7987fafc059e56b437c9 GIT binary patch literal 630758 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%0Ad%{9{p*RQ19eY_h8mJYH@Pb`a@pGm7P|iB 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 j^Wsghu)qS{pp^jQA=>wq9=NJvNT^bs_C-k@U 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%0At@2>3? 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

UroIuQ`olzW z1dQzfnh@$%V!R!MmD@?^cj|^4GtjrB*w$#I`uHIN2Wf#Y#$P4)5o~+{NH$599dK(d zr0CbNn@=&!hMa*jPDdt%&8cT?Jlm7!iVn19GbR$L6F67Fh4S~)5NpOkA{^uS^qR^^N`&s#Q z+GGmBw;!_+E4Mas^y>%0rv3^ynFALm(FUmXd8Jz%Ss1CxjA9Yru`ODR(OH5Cl7i`! zITn`&#CxdjB*CaODL#Y?fk_vrN$qtm|4{x8)n9;{61FuoYi%(bseu2-m5tE6S{vn`{&&7M>8O0swX8O5%5*H*{Vm^ zg0`Qi-DLutEMq;OhGA(8OzqCmL65)E8i}&M({(pDz&wV9X$>l2bLf3EuB&#PcV4 zDaX!HxCYO6-1Oz(N4{%>J~TOVI2>Q(&(d~npmpHCjY zrv5T;^zKa`Pt=~?ZuqT6&qH?@^zt_rOwWr45}=YlH7W(Re=Ro$%U6PI#JqekG5o)b zzkwa9wu*T>?jx>NzUW7&nWYNE;l-rxbW(5Suaun7{kziBB9PN=5W0UB$sOlUVgIh@ z6_7O$?3viDD-0gQ&$__{%&;}`$hVV)Hx-Hd9E8Gui^t~q0a7wWq;e`(QQ zCLjNzx2^Q3*B+s>udsvKEYr~c%j9z$6_&#qV>Evp7n+0%%})$lp~cIQk`}er+}rP8 z)vRyM4tDGo*Ye;|0Id~r_Z_5`QeFGhAQ@D@xjIvEP}Do3q52hqJNSjg8?uHXLb6A+&oH(^V=GC8C`zJ)N}Xe;RHIasYAls@O8eq^4LZ;BeZD{6 z<8yt!$LD)|uj9I|f1n>6dAh&u_uKvcu&mf?QHKN(dFBwU5g~InH%9KRm!zZ8P3E0! zQ5Yj-?ylct$+t)PK&n(<^Kkxe@0|F41L3zj4*&d&G=4@0Z|E=-P78Lx zCyR7v+o}|99GC14u`RG^IJ-+7GuQ1C4rAqla$}6vx>?Jvui-MH#b!-(;f&k#^FsmX;5l z3|{NNR$eYyDkalM)eft~P81e5xD4s|4mgV+e&1xGs6v?PgyRmu^}MRBvoT5=0G?lg zTyYq}am5I@opTD{olamN*SB4Q6CaHsRcNrZ*!NX^C$cxBRIcpr?fp`c>hJf#({dA5ka6!dGfd@r7r>#sm9uaMP_aCDA+6D)jT zu;$VKi-6)^NVETL!&G|)WQigmM8dgs`N&c=!OpW>?v04T&KbL#XN)6vss>6y7qL#k zE8ttIA{hQicLNm_N2AGGH08=7;gwGhq>-!(CH>DeziKoT-?7tC`DIkh*&HYN)L6d0 znprqO&8vX<$o*nbKr#*2o!5SI`+jVF`#0CFb359nxEkD6zy)hJe|~pi5K~NOlyr(4 zigvA%M8g@1zarX zge(E44g6u+&y77h*3!lBT+QtiDu*D1N`wJAmQ8nP-6nJzkSZN9tn9e8(76!(k>Lsi zPW$54#6R=LNvRC9AjEWpc#wG9RdLe#_ZeDTG0bJOz=JjHVQ(8{uK%P)q##~{@lmDO zmG*unltZ$BOW!&v`bd$pBBOC$iy^TuWaGM`8k!_`6AVBckTv~D<&?pO$B%*7{!_P2 zr{!B%cn@9X!rlu&W^;8YY+)1*b^`thd`@uxWOHzqD6B1)j|9 z!Xk$@S6_s240@$u00oSJwXGDAi{U6{U=0|rpvD6mYCHhvsdvC=k!IaLL?0_E_W6ur z4Cxw3s?8~lyDxc2xmI8KgKBHKtpOup0}ctXqGLu)G zly5BVBKNc)%|kdNJwzyP8xC#q+Jd*kTdo>(O6`{VG#Ys_=6)6oU1@5|028|L@S&Ij z6v8`!PCC{*|MSxm=6Ub*{Vb`Ca9Wmjfc1E;*<16Fa3t=cb$B_8^HkYB|6*8y)!VPn zD^42sOs_{WQREW=?3Har!ps5iNY=t7F>wzqcu(y2x9y*QeSLNDyWo4Ml{2vE$Ehta%R4_lj-+ev8h@l%1f9T`PyGQlq`HZj<{>LKe|g<{Ea26J%agxs z>G2Rfb5|#!)6OmH$gdywHp#!e9d4xLx1V~Waqo@Hlgjz%sfC-iAOVO;f-$A5`+=bo zY-x=UOT?iyfC3l8%C1)vB`P@DDdDtBrjUk=Oi)W7acbjCCJJBipqP1%6*&%r%VaVh zc}`#EG)^#Y1^4`fS71NXOJp$y6=|larmFFE5?d5Eyj$DIUQg^s4=MMjm>)peed#2t z1|B0HmnO@f(;~&Y^n3!(_dars5Ko4qrDOOEPVu4J_ zHSaLLeEq)@Z2zj(gwv@@J+0r)p|lYJTcyG*+`lZeTlxzPgZrze<+wo&X8j=yVrzvH zUu*P)x>EJFnEv1@F&!!e8}ZkMtBQ-3Z2-i$6E;8q%v>w_+J()x59)6`8s;&4+MlhH z=78By67Am@QLwU&!vx15g3a2?Pz^i_Y>p8xKh@eeo(^u!6eEO9jsK zF`^=TX2|7|v70_BM@Yj=|ICZ~;hVI6Nw2BYzBd-EU9$+T8NLel6IRS%At_$6rJKG4 zZM$i5jau3gXY@p8mEDuwMYGYW2CI$dMR>X(IsI1K<|HX@6fHU1bl7V>uY)_;gW%K5 znOFBbA~Do6y!WgqeB?8)YK~_(4fc*C!7pwQjeDb6idR8;po@clse(Km7&k<5Q(tc6 zLLJ$DYnseRL5}`%<3}m9eh!^>@dEj{joh>bPgt(4DqmTSNtBX+K3te`0J^%5hI?D< zx%`NOKWBkk^G96_uPF{KHHNsgc#Pfp^{bXH)@C9wGGM`jnjDTSYFs~1Slxfi9V^ib zl0XGIXcFn_k!ZI6i0FdI)4zl`DD06cxkJ9))D>8ZHg?ICK_i(#cmsW!*!I}0{ooH| za89U3<@_Bd-~t*cZ;6aKua)e>y%w4KEPJxRp5Fdt^P1Xz{yJcivhTih#vmS)!X1$U zWMM95U0a>}v~e;p>?}hrthurVtXnCPNe+uIK#EZ#hkB{III|7nFhM;c1*h^>7N%p! zX}Wpa%)Sa`hO+J~)BV8{yOReZcG1vw>7s-q)w7^)d~^oZmtWCEazl4pyEx-2IV4yk zwi}moVg}wdcOiI-gz*;J?wjV%cOJw^-i=Scg?8j|h9qdzwR`9SwswDv99NvN-m3@i z%}`{J$TXG^${Pf10ejBRjif+dh^*AuJC+~^v*j9z*OoJ*o_sPP$}ZzUdgE)Q#oxmxkGi` z%6q}PmhXOk{$&zt`Sbyi-X)gHJp1e0S-gaW#p}%~C{e2rqdoRIUtSvBo|U+ty3zYp zKcT{dGpC;+1#6?-3X3QVK>_IZn_N8FSY^NbXOtRt$l}AO8LnbTIp|gyQmK-N)Z%Rw z)5-yla!98Dm<)WBE5=FckTsmzBac%#bxQx4I4BGvr~2D8>i8 zCUe}<-%sjByTg$Z7Xow`Jc%LdXl31xi*xofp zEx2o^EuTm|Qryog>~t8_tXA)+bhP)uu=)I$l*7?Zm|({h%by{I)pIwNMP_qQy#i%d zqn}l`3Yp3*?RN>Eq27Z_1-l&Le(ty*|SD+tyt=#cL@U_EMTc^i5j;rr41Jf0B zHWl_e91oS!AN8Z`cb13n-I7hzz)REIX zK?SvKE2-32Rd5Gm&7)#^Ja0+o_4=f#ia8%`pAi~5ymAwd<2flewPmbk;O%tATA`y`gqOOg>olM)Dt3`RJLViu#z>vM8^J&#>Tn>$ z;Lh|yP;LZu?PV8q0aQ&bxW6Ho?li8>n-gjG^JC6eH;?Ll=e75soU?Xqruh-YQ|ky zDN&-g7Y!TM1G@F&x+GOK$3p=hWaS-ygy!s;KI+ln56Q;vC57cHoWs3tDQPxxcnifZ z$xJE-=dc-xBHB|FIm52Y=92FEMTq%{t%@TFGp+F$z4#bLvrD^ z(3BI?YIg)PrQZ=ByAaf=5pBku@v+JyCX-iir5 zclo|Vk>r5xl-K058J@j+)2qISH<$DzkU(e|jG+vvx?BF1x+R-+MJ%|M&^+g-+6lP$ zSeD-WkB^67{jVaQ!166x8z-tX?6xWV46|Oe-q0M!XdmNfdMFz4I)D0FN)rb^E`v(2 zgSq2!;u>uz%O~h4X8dW6nh>y+<|2H6^qis#|cf z5#0#6m|$A}gS%MQL;8S5^`zvRHi24neOlz+*EX)P8*|Pi1M8*bvx1gC_X4@ALTCk- zhBZu$RC59{^|r3DpWYQEdZA9t2@;NoD3-|Es?wG3%^iA=c zGE8oo`#7P#Ii~#J`+J zX$&dZKa*yz6PJjGeQ|ep*-&`&M^<Z26)3A-BYJAyrnw^=P&Uj?@tqD1W&wpt$hH|}fC3$fcoh+-c(J%{=ZXo6&v()^ zgW*(tKsphUmiwZGqL3IGPAK^fnSG>j#D;>L5r*V9(FyptaRWnA2!;dj_?Ft?h2fhm zdoa@l`^?E~O7eozJo|K-Cv=>)!OcsK{;Xdp))tz$xj6dqzTIaGu^$veGW)<~B930_ z>%^-CPGW}-tvchygtU4M0!>n~%kUW%iU_HMa zbU_>26ILR*HVHJHA-h2Qm4|kR;Nw(WZEz$_NXn7NjgmWFID1^&SO?Fn91&R|oQ_$B zvhHfM%4ML1UHRR+4cfr;?P8TvkjP~I7MW7Qe73kFnucVFT;agG+b;xvXfT#o(3kbz z5|;G=92_Qt2_HhD-WbscI>U2|I{*Vd&_}Wt+RN5%b3H$b6tD&=-6p-^DvkO>9WRV3 zy3X{0Uzk4G7+Wo1w5Gu7X#iS>w8C9!BWut^F*#$1A)v5#WSUgg{kKB zg8>Qvd42rbi_+$Vd?&5C+QPjH2+~W56$|?E>X-4H>(JY~bW}1{YW%9;)NPOI(Y+Jh z>d+UpgS-M$`=pWH&$;2#Dnc{2d~dZAb-u90J}WdehBRMk>&pJrZtH&D-Wfc9x484z zaCFF1O*OLF(aN)XfSdNN5E|)M;=`9}M;@Y6-5Z^}m`p4ynlEI%<*wN$Gijbj9vW$x z_ntE;@)-rlw#+yJrIO6Z87zkRkp9!S4WcZd$y3!h_jiBP>dnq~%Tm zla9Q~M5#Lyal4}O-t786xyfTL5i8C}R)86% zaCh&OciDq%pj7Aj(Hlt+K&d=XVCi_-EI_7!)?3}--D>*8{qhn(X;NU1WHfew07j+Y zz|AY)Z3F=2y!7kbZ38`z-yP)^r1HO=>kUlCd^sSX5&6hCLB!7t1UOaDQ(nvJNWF^@q!$d3aE+R(GQy}4*p}X9JRH`ad^qMkCmrgf89PjEw6rQkbKk-af0KjJRILCYE7By02SN7 z8T7=>8Fq>jQjzCrm@P8o>8z>Cy9!^SXoGc+uV!0&h&Cbo5RQ4n5RS?CuvfkM&D`h) zR%rTji&JgwYr!lLZJoyr<80{e zZS=CBU%nVsBySr=ZScXxXVnK|FQtSdIooLwxZ-az;mxs}*M9wO?_MD)&yBul|NM4` z*Ba!!E#OCV|3U8!O|T@d*MIlp=;^K#d$sCb|N8zBx_i5HjhkWn$02QnG{H6Q&qw!a zzi55;>${=-YR1=@`*HI>!G=Fp{rd6w-5(p@OQL`Nvgg8O85-e0_mBR*tB6ng(G)*x z0NN8(M^R(grnEZAUn-<36; zLf%^sYE+#?Ie)RG<>G)>kDkX}1&&9<%^SQ%3Ujb@4 zs-;2M$e`Mbv|zKC?dO?){~BI=?o|Jk6+HGIJa_-O<7c)TOI>vQ2*@hN;#J6vnop;) zq(MaE7aZ3I$fq5MdbD>=)#Z@7eI^!Imd`?IsUTeif+;Etvv$7Y|x4M8m<^tza(^Cc7ivb{7zalVs@ z*?kA?MQL^;^RV&jRJ`%q$^K>6sHu?A_gH7E+8+AUiM?u&ma;kK#Ce0NHaKF z+{|vdQqU?lBX^;b#FYxj%{C{TEAP`bLwRq{p4GMUcxJt1hsP<#bh945@XU+ z-Dk*{y@yvE+ggppn-eaRvWa{P+e9qaDxTaApE zEKkY5BLicu!%>l}*j|}%ePa}@u2u7!%-Jp|<0#TdT%pxaTloF$rO!9Nzq=|dVL4NH z@t!qsct^vJ_tGKbmXL3#xC=P#+x(5VzV`c%PdD8Y?4DkXxYAAVbbe&>3_ie5xHTwv z?#Ky${3LD|-o1^7U7pa1At5zj!e-N~WOG~v(9hgC<6Hrk%Eo=Qy0wF^ee|{)$fnb+ ziRHoF^4oc7a?MO^U7Kxfc`uhx#@QzR`tj%J&&Ep4!J^&qSu6W1#gnP&SQ4KruhN?j z5S0K5hn70;=FqLAusp-B6i(L462Yv^#gL>s^~yR#Josty-BdVGfR_lYww8}Dtdj0) zGgYzXWH<$+Ib?F#;+9OwNu2FT=t^3QbS1ny*dF?A?OSC!nEOQ3Ml#(FsW8JA`tb@B zEmq98h-V_o7>^mj;ooyP%?7s*zJC~1BV@eXlfYcMzHlsI!!#VkuOw* zX6jK@_1)IdMRcYyrtzbAa_Q?$ujARe21p0BRj$l?9-YFm9pWnyozx6_u_8Hn3|s(?dh{q$GO0N!l~5-Jd5>WZGP zEJV{4{Rgmy$a5^YVBOG3#pg+!(r&P7q7>fVAxsFdk%A5wQ_NM(t9h)*+T*w(40x1r z?iOKr-{NuMp(4N%ssb}=T_z*kjD>WM=umqtr=&f|d?ke;zLG(zzM`)A%5hd@1E|$qi?n(r}q>n8pTl1QkXSW!>I5Y z^*<_e&h30JS3^Q6W!lF0hxd&cNn7J{#ANf$H#p4UVV+hX-*x-xz_Y5lP%GFrr3AkB zecAo?Gu#~q)0p2L+`p=ThJ`)9{)BO@0u}ix&P}bEa$d$9Z3xDz zp$3CDK+(I{%^VlCL23CZ9cYl_=OD&kFLaKa}AzQJ4Wl0%!h=7>C$#Mf_8_meg5Cmh}P z=hVxc>H3~hqs?xDg_nXUhofUI+fQS=sG+wTW!DD0IPSY>%;uGy6((QsQ*V9u1RaD! zCMcghKdOpA=`e6Bb7TqhnX`2;k?>)vDNK=eEWj zFFZc3=Sl*5Kb(oHgl1eip&1v}w=<9f+k|2s4);be905FGjtjoUAnk!YD$^mczXSKN zUDtMH50lla+lC!#KML+15U0xa62044%1@EdV4$ywCoHf!Ih6-!>O(PmEH*5RHb1WM z)MKHZxa7Df@xAF;gGfZHd<$`pbcZnoJXg`wrKQgPB_6%=9wJyLUJh zOd*_mC3YcPr@k!M5VT9`XgCka8%>P*fdk`6V*F{Rhf~%(s~0#ehUw>>r=ox*H9fD` z6MI?-lG84` zJ@TB$DqGz0!Tz9S*h)N;-k5XNAGGu+5beungaSriPHSD6 z6IzJWe@kbKXj8h6Sj|l5Rd^4077VitxQ|hpA4u?|K215tu;Nu0T+^1Q25pS`Ad1NR zkCxdc*lW&~r8FIX#*<{4UU2`G3zSP0t64((fnL663S?lxK|C2`$gWJ8Ks`2!7IId{ z3Had;{gsL~GVXZ@<>)Rz+taK&;}WQ z8)K-U%s?4&b$|1z;Ztm@R{Bt_VZXxMbEPiz-v&$~J!8S)3|@YV6)>;0ZYR!x zQD&Boqz#Om{j=RXRD14(kVt$5;?T9)HLp->W^5iNA|_8Az2f^kw6FltmUbi0)cXeF zu9(WFsq=BAh56d+V?BgA+R_eVB4~x%o{+6#iMf@#PE9>DE!sK1NIu@%b4ir%O;u;T z#KnxfxC~yCT#l)Ja;v>_i{?7~Gjww3?DKmY&5|MxcsPxz9oixY&f|r8A$OA~o9q`w zKoOw$#o0mtO$&$IHW9BoD`^m zrs>0IpD;)eSgyv-em)De=%E9&a~lVU+k6k0^g6Yw;=2#Rn2M#P(o1wCCK`Wg zOJ%|J4txgy8>TT5QC49dVpiO`AuBNhC;Z0mH@b-Crxqe~4JV#>blC+3uk)^wO!AMP z6`8Wc7ru-VY~)MHm1m%V_ghvoxMs0cLNU`(c2o-FWGd&;>H991K~Cly$jNNloxB|) z;p3a&!dAmLfCRr$z!rZ2W7*ipaE9y-!gt9rYnDt*{V2KYF`C`wl{~>tOtmrcylep; zp$$*ae@MZ-|L${EX#OLcM9u(tVmDp+Y|!%cbvNoE6st_8`A5^whE^lRZK zR_PqJ?H<(c3mipju!Cb7k*;AzHq_>5VO!fRP6MM~Wei^T$JRcim`o?Z^_6y6m1vWs_q{M?+>v)7}VQQBcUB~0ZbbTw- zWk%zN(!k}&c)izXLw!3JhO znR_d64Q7;WxZ-$bJbXi~GJ#V22&SimB4bfSZd$x0o$2JHkhB&9gqtZ|9c+W1Sf&R>)h~Y643m zOy?*q6Euxa$Mxzc+#0DQTO_6{lrvG9No-^+uS$34q_Q66*sJnMkw`RBwnf?rsD^yh z=-K@GSK@fDactrsLJp=u2eO&R!gWq7ZyG*=tp;+_DzdDf($=c9v+UugCJGPc1lqaj z8_DQbN z?sOHn_7FZ|B38#nGvAGF?Orpj6<)>Ye}heP%ZdAC=wU3Goy*9g`+Sph-n`b~#s)mo zgVL;n*jY<&VgnLTB6Iq3t=$l2>q)*{_%=e=LjQ$(66jfSX|6_Sv!+W4 z`QJ$Le>}1OduTwJQ+Nk%$dNH_x^mJYgc*JitJD4(L)jy|%6CYwlU>NJRAj#U;^?fgD(uZ;JJq}cPdw`&NH8hjN(R(n z$3Y+_PP`53u=kLG*fMVtsEW4Y0knfZspHulOC5HbB*4&K$uppwKjEodc@H6QT{)Ze zJgX~H^NXRb4Jq)h11i4Yldv@c3yH|L7~)2nvn)G!zn{Wg8x!b3J3#Z;s9C(#cX>~S zW9=Idfkq_5GD``I%hAk!^M&!4D)J_K3P$YpeMKJc9W(Rf(H#-|t)Xt~dH zA({8A1Fbafd8bZp5^@EOn4IjD_(O57s|cB3K7B$rX1++2_PP2)EjT*11}Ey4Dx!Vh zFR%3vg0xL7EW4;a(8ri<2Y6TWU=l`1P6ikm*!1B1B5KKzO#XR zsEnbNVtE|67)>6pAZZTm{`tlACf)WoTgviBr2X|7Bzv!+j}M|-V~YE%qu+dTQp0(D z9!q_G=JR8XLy%MR1a*YQc@j~oXim%ea0aTco2}y!IeYK`x!8R-o}3>c$n8@EG-ej0 z0U=}d+lvJ@Y2k@&>|3BQ7qX+9q^04&ere#An_1QO+3FJPx4jCBn>JLzrVZ=I$DN;v zTzxyu`HPOK7-RaM)6?m^k=FT3pO5}le(zu!l8RXf*)O)Et?~lp3tC+j+N8AskYB z{)=j_{$#Kdm2Y-pob%oizs-tjjkZd)^V=3D=v>P^cj&9%2EdXBk1ODzb~OY=QXd|t z1DgHT8`RKvqcA%*U)^@)`vUGJ6_sP8|Lhg||HCh}lMjR@_M!FBR7F9bMd}vSRM-yc zv&_(_lq+eP!3v5kXD;@* z{VujVzA__WBZ9z~*Q(^SeRz)Bh_k%jIvPi|sC*bRG-=l%{kXtz{=`p%rBJ&{9@>xg zLM0Mv#fLJ>E{mGiOY%XMKQs}=MXg2xS+wK}C;3}QE|7h?dqrB1k7y!kRBT*epH3O5 z9f9oA+W{E@`!pLwg_j}WCRq$PTpkB%AOxv4f>RqBym@Hsc=;D}w>xmaJd*&4!9^|T+9ag;@a2U?eiFA$*T1$O zw;q_>>FgJ<1wCv>%Rq8_L`Lr70LcX{D5BNz7z{(|ciY5pnqvQe@u`ecOM)uDZW~}Tvauh0Ty}YqQoT2jj8>ET#3o^zC|qO}24ahDFQ~#F-zmX}A$}TN9c9HM z*#szdk<6##dv55w;;?aah6V#GQc~V2s`+lD^IloFD;Xc0^5()O4K~BnT5kUv+&*m6 z8%F6)>}kPl^v{#+M*W*p=-!lh4-^UR9fBuDG8iZ%12R!3c9^~WGPxfwBROSX0)Bm3 z8qsiZQv?(bL815XMJV+4;Ji4oXZ9j^A`TLIjeXEV)hvj++l2LP>1%d2U4DCMkK*?k zfzX@n7bMt&5Fz=2XD>*wA))uTc!-~+_v_14tGWP*+_KwV$1kAu;nTi%6g}7&_0xN` z7&!p<*~FA=R#$Kg5RSxs8@;uS0#T2|Lee1BhAP)adw_Q6xc`WWK!hOOu&Jw`I{szh z`9T(5ZH35WAMN=7D8ki1@31RYxb$^n#@Lz+ab;zW7xzTQjfWWN0?(qd{ZMTEdceB# znOzC3T$zo|=WTzOIpNcO>It^NTx{&~=UYLdvz0+R&~NMgKSAvcW-{i>ypu zFHR#@7Jcrt>@CvXzp$srJ_2)>N!<2C(wu12Lo-QyQ)XeVKQA()3rN1Q8byuh>lcDs zGqh%;&l!D!9R+x=%$7rYdY3az3JKBQ50y9phLI5m)?HW})l&*#87NwF;$XzA2fi?` z+qY3zhQ`FECDW|p2=i{9-(9kLbL2h8C(y5!IO@%_DmzNUbB7hMoeY{EwD)r`T0-6! z5Yf?Wn=JBLAQne9@_ZO}CP#}lCRBRHw6X+NSy;}$T4hSN9j-xE+3qL`ooQztAE9RV z(U>@W4BlfCxfn)gi41pbjIZlch~gnDWVc8CfNoty(Std%+ZnhG$Ms_tw2_fO8`-=( zjVPH%xB+fRkWF;zSnI82;3CU3zw~J%q=t|j+bSXN0&C&B9<&ggRb&%hCNvk%ya3~q z!6nnIJV{5TEE3NaH9dOYo@RqQ{8S!yY4d@S_n%PRl42|#4N$-m;jE9VRO4|7F>Q{d zKm$2AX&}9E)~~^PAZSjS$yN`0{k&&Q0aCv&pT$7rQ{5qhO#L9VM5@zTuT5l764;>8 zWr1Jze6R_#QZQ~0UWSN88v(Is0}+cuNCVfTA)4Uc2IwC0QGh8X-y?6z>mhn&3C8UY z#t51#0>7;FjbI~L_cQcL3GQtOw9?K6zlA_Ugbc6SmlIRP$Wn{tI}J+b73*OJ$x> zy)<-I`vT3W7?XyEt1WGEaju-j8A z3JqY}XR#Z$1P@~pU&l4D;zJ_C7I=Ti?4gf|tXj4patlo#W7mWA$Kw61ys zE{TFm*42FNic6r4FiVZle{*+I#uLxlm&tU?%{$KI#u@AV#D5EUB5F(eh)!WAA7)jpx6Na*l|F-B+|!)~efp;CdW zy~(n;BxH{yKMUuviFbK62PGff;T*yFw=hYUZrDWFEJ&6;BF#2M0WtSk;gDjn-3lp| zUTdiL!QGDNm`#@;lP~=xR>W3``|GEjlrB@ww^zW=Y^;kenn)dK5Q9(xYh+ zx4UicuG0)!WgmvzEO&O#h#!*LI|}pDEqJg=nAsy0*QaYYlJwG^E@9Ol--Q{vEwI~} z!dH2OYYFkzC_|jC;8Ux?C^46xD5^YA`RWTKa@@3Kt~C~Eo9D$>e1$%>@0E%rsei8InIVD&z~ex8I7_8(2p#j6`<<=Kg@izU><00V^$q2odk z5A3-;<9P@(C~z#EWeayNrw{cz{Gyz!TF5jHEoPcC)SxFC6=*%rCZL>)9^Gt5uEt>u zf$?y0bD?$#zx?WM3KWRQSZpglIc)xAW811dNR zygPc=s2^pkNN^_whhf8XCepmT5OQ~Uy=E=_7bN-bq+rwQCbziSc&qa5yxJhSb{@W|azkCr@FfzowQ6!k}U-m9uzq1w;X4IQ|qT4Pv}2pivt%c0+f z0DvBOPijteHr8C9Fm%xXNU<3}${j2@I-IE{KcMOn?s(GOiKd6^ple>KHP5eMAM6Sv z^$AQT6^MbsQs9=j8&PYGHk)y1qJg-}o6pgO^yf~PC!dG#zYl6F(a)hFhTwkH1)kdksxYGyrz7=66Ru1at0juaVB_LjK*`{?typ zWshVuyD{OU2WU(07haNvlJMZP!Alp0pmpM075n$quO5bP)De;?%JhElAYT2-(mL@l zQ2`Hj2q9o9oT)?5KB@-3a7F;ib5Vt=<__ zYYCmXESi*l@I+qQk`vXnjkq`KIOIf8r#zLL>;;h|ag0O0n0@STk!0r$K_m&a^yuJ|7#43#(5!@LPM?CImpY&c-0^^-MM#(q3TGpXeX#9qH=btaN-> zHklb|V!2ozi3fhR{1g4_ES&pDV@Q}*4lQp@$u>_6j6E7HjdMpETZ^f;Q@d%96iXBR zlHd3StGoUr9S0B5wmE?d*{92cUs+OSkUN#s47pP#xZCC8#hs#Blc}Be=_!v$4&=Q@ z?u$dqzFjSHhag-=tm7x6G}l0@%(YsVFDryI;vP9`n=e>VN-EJ2FL)FUim5$7}Liv&AVXMPjj%s@q+kJhS5auAtg|<}ou9khz zDg6M2j?mFEwFbeK3T}GJVV}#n!cw9eZsdSt9=g)w2m~v;BCmOr{m+Gt|9qhSzh?LT zn`PSO;zCHK#)@p0`r=^IgyUf=#L5@FprSrh#XguPPLW!4dyZJ zDprboo0f&ZJRG;38dEbdhlE^dpEDFT3mdubTBSUqqp-FDF`sWC%R(H1R&ur1Ut+ zitAmt)~5%hZPiJZJd*Hl1F7!AOM!tjrOtD-YLw%OYH~LRic*VSC3w<{p|QU(OcMjG zQR{+}&`^ecjEhvnb8H-BZ5u=b-qSjByoF0RE7v%x-gYsIfhJx+L5@fhS&fntE zTd^sv*5>O1efL#ZuIu6@9Mi|@&79UJY^hlDAv0(sHAp=XT|YQu;x8!M%xhkT{_tpEP;a4%yJeTX4i>9tk?*B<)u|3$ zU5<58nYeELWNv61@St{zI4Jss^5tG(! za>0p3sw;WV%=#Q}TI8HB*Z5ykoBg)~VQcOU+_-FJEofl$zlz>Ez`c6?C>I(ceqU`? zwI^?>fT0q%SinG@n-|=Pz#Tp;VVs1ERARoFadbv6W`Op6>&vv5xz83OKmZb6=(nx^ z8Ub=ZU^`9#glYT*gz+f5`~M>#%yIh2-_+M45QeK(pb?S$UeJz=q~?45l>>Tf7ALRy zmmKimKXbr~f*i2@WTq3;zIX;jfL_xTuU4{*HG#C$$8TFZ`_i_S6CJ+gjNKKNEQx*(iWm>SzosuZD0|j= zu|RR&$(y%uQ1(qF##(R$%)uA3x|{{F$r+MOcSJ*IxVw%KXFlGexsMC1F2$`QWPdBu z`xlj@3>OTpd&(&N7Y`wWD`n772M_4)c(nM13biz#r%wQc-Bh#RE3n}?f~T+8*AXea z-S<3wU*avXdcBgY#{rl39G)%!&Ag}xaV1oRt5j8AteG}RrGW5a_dm$4TAlB+b^#Ac zo!XvQ5wvfi=djw_q01C0rB}aUW%^Uj7MRA=54S(k)tPms*&%^ZaOC1kY<2VYC#hO< zpQV`Ehcsa>FXUr}{3Dhbbdu*zptN+Zdpk;((hr22d~G_j`%~Al@ZN}4g=Ro&*@qxt zEqFjOiMj4LbpB9wacNn6XYfy?Qb!Mi6RZAWVB5lB*eT14RU`DyqRz`rRT)GU`NN9s z(+wGzFeueS;ScuHk3*@RYi<=WLXhf#>@{8(^7;Or+ejKKh@OLEOZOUwn(mmw73v)A7QjB(g@aS6^vJ4F zV}S$m)m%0;b3_(8{z`ik0^6&iio*&J)ov`41 zAxG750L&N%C?H-W;L^-}{7V*QPXdGn!d2HydHCOx)_w$)Q3Hj5%;5DKetTezR<&*! zHp_*fxlbfRm?lq{$-AqI*kI8UAJNoh~=QibqR?i$~i3to^1mMrkVE-b`vBfblEc__Q*fx!8C2y-d; zGzN>j4_~y)ldsQ1%8@c7I8Uv&D+PK@8rrVT9^H~358uV_gz!?qmtI7}wuwD)0<@s0 z$9WMg2w%9^TDzPen)mDn-Q-Sx?zcCmYIGP9mnN?e2Ejoy|A4W^%xI_5YyWVi z!U?Wvd?2D9-P|G8l1{m0)SvQXNDo%6#6WqKKF5E!a_3FCHV=)=)3rfSgMgvbxcN8J zA9T}+Tq64@`)P;2N_)cU8+8-RgZL-*{j6dIW*qg#!+>NiSN0wU!s9`Aa&N}@EzSEC zgNg!RA2Yegn`(P*7O{ff8j?xpHu`CW+5_gv9EY$oG0KAhn68x0G&&EGN$fXGfx2A9>&Nx%@`pBSAJ&N@!RIxBw3fZ}s&^d6K*gmVx(b*M#ct&zsNT z;>{ixVvrthTQCB6Kq-W-Br?3)2|}3c58;_F1klrqh|m)BOHoA2Qfv>}g$l1bxqAAH`-ayHJ2`WH z+6j7^{FFntm{vUEjmAtm)oUH%yj12dO{|Q3L5FY~KX+U1alA675*wkhLdbN6#jxnm z?XApp%hdJi-JYVPQ`FJQKOCRnsPBHqxqgooE zG2ZQx+krge0hCCLJ~O6$I~WCxa)X~iP^M|R~a@n3+N z!E3I~AqgG;8SpDw>iOxG9k(=je^dWvhb82e_XG(;dtR}v!>;^OHHY*xr?8&){Y-Vc zKB`y7Fm^Y%6+Qa$315J1U#8Qkfk?{TYeh3$f3>392Lu!{4@(*7xj*dA30Ek!c8s(# zGLl0|~C>Prh*%Tsp6+9oke(=|p*|Ig~nZ+NDqV#WAVvTpj#%$<${3qBNaU;M@ zF3o*pT#tmchStX&?FpSwz1)=j-#6a>;1c}r;@ST$p8fCQ+5c;cXCG9j4|Esg7zs#F z)NrXjqSgy2^=Xys{4*KOs0wT!XfDgZKJjCwWYgIUHD_mH6-;{R-S771xKD& z)lT)j1Z%HNY=>KQt(N|qX9x)ZKrj5p1z;Bd6jp;#D`@4yB)!n6S{Kh`S|| z4!cYg81ZGda)}NZ8m^bhe+e8zZ~xjdTO{e#S9s=XZd2-26_$GO?GEBJ|DrklV!Piv ztH-6Ebh}(VbS}6)`bqcYa}O>`FG<#Pzf$wCKI&fe+8(8vb}g$%*P2OJmJzNcz4%>v zy2;jUIgD_WY}4AA4yIXaUkGiq(pY{f^dp8q-o13o)85?)avZq!U_H!nU_;g+Uv;pY zv;a%&BkWLm3ie zJ(CaN0NL#nxF88QmW~15m{TgdV1$sy%0dxw-i65jhU+o!z4N^JCnw#Y0mM2?vxt!Q zbR0ifWrz|2$j}qDXl8L|nbPK=P8er5u*7kP$2%YyFUISDJe4zP&jQ>$ihMGc$qkY- z4)t%yi${nLM+EHJuS`w2k6s$->f8)xHB$|n(=O^Du?sEXF_}W$h4-gQywKdu#JgWO z%Us)y7PjTfU1kwwjnqV=YT)W9rZf?xpo-O!$ovx$u9k{?(_=f5wGGo%0BuAAjk{AW zdyKw)<$j1-pq@mmIQ3GsGr7z->NfwhK0D3;t5|eoNYctUp1WCtw{oJo=70qQNS$7g z%$GN)oS*{Gpa4Zbr~#d&DqD+%2(6)&5MJ^HpAOK8*L-9O@s3rl4+Lj6DKG0)a0QB+ zkLPbe`NUVcWa2D5Jge}>aTE&#;U&Eq6yj(%v9r|*ktJ=6=JRuJmOGlwhCzx5jW!8Q zP?CshgltUdz$X~T#W;&qcvXlVB2W>E4L;Cz`5Hc<9lV{NM(AlFyO(IQnL4z7x`-Qo zsjJ_2<)lWvgltZ%wk&ufTL<{5O0z=E(9pds7?yj4x2B{G1fLD=FzTfv{HBx3!3)g*x;@()6tlT z4Sht$V}=Sl;SCO&u|`=90stRow(eqQb*MhQ~93f!r4P z%;0V=unH@(Ahhwe(@C8QDkMHCy{PW{tz>;;8j)bm_=cC`t#K{qAxTud$teEPy3Ir< zi17?;11zi)Kr5EOG3CL6JT@D%5@p)fqp-b+(`W|l7fR5#6K(?*gT#}9a!2B)yy$%r zF78boF3l5ab=eUzB?F8+>`PpV%5v?L@i1BR_7%Ls23>?4&l*CeCooX1;8drIV@Xcc zKDOcvF%7H@;uk4>tZbZWO~bWTa^DIa(o_+aD|`=qQ!THK4(6e$RAF*uD`KI?kgjIM zUl}=mP-045W_Bs0n=++{{Ne~GlMQ#^B9ZZz9I8hcmhQ$W7a4SDwR~iT8VC2KayfB>3j#5pm zve&4)-vWjug%CIoV3Fdp_7fh$7qsd$b++v2R=qO{_h@?wDQ!ZmGeQNl?j-=FG9pF$ zWeg?cXbxxEj8sb90MuAPuUADA&onscFw_A?`8o9}>jC+21HSgs5=x)F@6yB6^>3#J zl)`(q>g+LvN(mF8rj5&em3xoTO*qcF2#&e@_6`n3cph}61))pRmiRXkRhbT;*bTC0 zvJu!Yc9aYmh5Q;u+!KJkjiqzR4~OE-`W?0X9>^Ss(Nb{Lc?b_t9t;AHzUB+1bwOCn!CfhVJ(qT@Cr+_tw%xX)dBlvAs{}dlumDMV#RCQdh zk+?oxz_N&M;wrBMVqp)ErCB5=1lH}pIG^&>tFlYi! zdcLF8kQ9599hVQYuHJE|<*FXl2qKnkrsXS`A>#U2cT{JDRJwQr8?i|dm0p~&VH(M6 z6t2LmQ#h@Z#_uL?UIlMXG)s=tGZ!}56(%O{pSl#x2_|8k#fYM=1irHI8=~yNyJe69 zJK;eJD)wCV2kTlbaxts1Gy@-sA=7;by{nGIO5T*)o?NdlT45cRJzek6p0DEVizIwa zj>Hm{jf+T`xx8SVk6F7cP$S5h{li!9{-BzorT2R$FHCulW15zn$%=ZzmWmurp1Z#A zV>)-?n_^p}qrUW;AJ;NH!bdZXkjrFi?(auhv$w==wHZYj3V~C~K#({=k7S1RK|iwO zNFdMwaiCf}sGPZG8O94H=?D^gTrK)S;m4Iak@K%@H6{xVkb!fJ5fv6hGr1BBkGV@y!M_bD`(C@&-p{_ zMt$TMXdyt+C~o9<42pz7g<&=>#6cNFgggT%mj~qrr-P>Hh-uy?7UsALP*BP9qd=z_ z8A1K&CyFwTR%TQ&pdxFi%mAup`d%2t)vEwkcu>Qx%qHyeWh9j5rLDfFT!5kuOup3>If2_}PT9>^kd=;A!-WT{+JTFj>Qj zqXq=O>72SKsJeFVG(DFQ22Srl!Fy1=x?F=oWyfijY#q8kC>yhzk3GgW>(3z|a_c5T zB3cnvaY&K8r3MobS_@`e1>f}Vd8YHnfl@x%Cg0RY*>oE5aZLA-OTI|NhsOBGR^0j^ zC1)|F7?PwdG%j-_qMU0B+=Y_GEU-h5l3I)YdMlrCtZ>=bcDL0YCbuEV(&d{5COV^l=5)^sXQe$%9b7#W2@+(n6(kXUv?({*p=NU8ow+*aSTebDOlf+ zlAZIkhkJI@(yKqj4uSSF+f9>G@&+#VF2xbMm?NI3cP(g|$;v4#?K5v}Oq zLIqy;0n(K#9V+lim`0ee)M_X)4;2eT1nQ4gpVql;(>^V|F@lK;Terp>;vOMPIQU4; zAeyY88>2F(xzhbq4SOJau8DBtn3BB^)iPn=P>$D1khv0mpQASOi!ksj9|FGDEiQANfz*2@K_GdR<7C zy~LV-66EYdB5+?<;&L-36Q^y`_cj$>XMKE$z_J^$qX7rWY8oIX(|pH zjy`0Yh*YJgR@h!|aM$ef-MRcR+vbCSwdQ)ux9i8pAh*hkm;v%3sphT|&9S8BRQMA* z2i`i@JYahxB8=F-=5)&Az&`gIBad&e!fxESW_0G^n+=cO+<3XSW%5MJ>+3CVAGf^! z)-tVr^Mmcp*)=!k_THR7ar5i-o8KSb{Q2!Bpdm!s3BgbyW}gsSEW|a#AU~nZcOj(F zDreWK5ZbD=uT`bERjs*IW29BwjJ6|J@J&Pc^CZoQkW?|8Sk}J7JyhI&>G8ar-2d`=8f& zCR`e;uD|779b6xHqhDLtm8TzqpAbLC^l9mb+o{9ZPjta*7e%vb`rH#O+Yj$YTZ6W; ziH_wIe>q3j{C3v{%w$jdh4EW_ZTC|iO=iE!XOpUY4j)<ByLLld0^=J^Ugyun>SmPYjV+2ny$fd^I1c);kORM*ZBi^%RjvtR{cjWEmU@4 zB1Ha*(AaFmO+ohUnaX>mY5SX7KW$y7W>LJ{Ls6*My6%ah93UTDQrbnJlFvY#^+I zaB8h~WcIM!Ze9E7OsamUV!s?x+J?X7)cx}8I7}>?8L{D^6Bi$ZTM%{ENYu4^nIpsW zh_uEKpFZkKrs8qxe$(i;b(Z}^e#}1Nn;4pF*EE|J4^mR(hBuygx1Nf{I096bk-BBi zsuGdIUsXAx)|u#Qj!&h-CH)y^l~bdbRpvkT@K;t|2vG05 z>}(@<1-*LejatC5QTEfck#d|`!)g!D6XEUj-d(&w9jMR-0mD9UuUU&{)8@1YpyH&f%@-9_hutH>kOrjnw#T~<5 zVNT#ux>ZaKL>HYk;`mQt9B+ytt!3ZidD{4F`YT#-WSCB-o?#8s@V&sl`SHP{F!_|} z6Yc<<%^YOfBZPMetLB4YM)v1&?VE75kITFdUq{=RXRjp_S;W25LisencEAFuv2$;q z^eXw*ub^PlUbn6%E6c4ov#!KGhtp2vBECsH()^w~10&sb4X7>iPHtLbbDU$#P2m&! zhwl}K6De6Ds^l}d5(s{sax5}1!mhoors@o&D6G=KT2qp#UBo<7tSBQrkE4hJ(u}YZ zxLLUF^q$B!y3M8@pKMoYNxHzTsUhxG#lnFMBo?z^%@l99FRkp6la3scMj$k@OHg|UZ#{d=ii?_@9e%p1bt@|nkljnA!TwA4}Qe{$6C$3tZ<-oj8 zHjMEh6L|qryir#PO_Q8wqVxkJ21`R` zr94`ZN=U)rneZ_w@BPt$NZ>F1<*b=DLN}BrM&?@&RI;Sc1>dSx@OPB((io<%$=B!O z_^CB<*>Bu0kWn(cGwVL(pIfKhp1x{S;1>&_^?}_Ip6@@^@_t}1$n^wQHEOFK+l(@e zNs`$)(pH<@SF_RTdQiRdEl2lJ>Jni|wtsImK`>qu9n|yazS#?i(%nm(iv+b7*n-U$ z`p$3N`OW*T7shK8!b(60S_})N63bWLcGUg;In&Qg%UyLM-#kj&l*LsW#a#T=Mq2n(x zd8B1bAhjXIDnLXF#>n?v)^*x*qV~_HRQ_)0GMc5eCXJD)N`5vP2!ifi%TKPbD3ukTMOTSC&G&G! zcCok_2o(zLJ{e_g>v*TY>v9_G2lFI2W=N=YwP7!fC(p(Zt>Zds5c>&B4INcSuv)aj zl590CkurM5tSI1J`J?_!LO=1A&BkGgGQ!wlwJ+%EV|}r&#T%s7JbuK;s=W+Eg-K;a zVikSeQwvpS603*RI118vt4&T2SFDY`dhpQUi@`X{qA3R*3<<5I;4JA#NpiaKzhxTIb*bTtVfVss@!Dksy)ieGL2T~^uOun`u!m)p z*Y01v^T|oGvujC$^~UR7bC+x@chyhKNr!KkyK;T4!^-JTk6(FxzGmBTddJ=CPe13# zH-G%`CtG%(%ecXOoBGdbWs=>v^4h_V}@x8l<$3+eJ7F5SqLR>5F~vCsUK2jrQxg)nf2+~@JIzfAw{AG#rsj3nZ?cUs zN|VHt&e;LvA<@DGuUaPCDN;sFb!NU)j_k0tQ_s5n<3meTZu6lx6z43?5(5P2pmOYU zsO$HcqoQP2U~H7F`iZULWS?~AnM0c7Jn^8!j>$3S9d#9h5{Tx05a_QD5|40TiX;^azg7Li#Lubu~E3GeT6FVWeb3j!&Jt>$<>z z7M~`#rwL0DbYQnkBF^zhH+dDjX+0#}o61`dKi6ClZNdOg+^5rjBS7}>Zd}6_%b#^>E zi@6@|-ol~L`9|-pOB=Y{pd8f;2v+_8##B?l#4LFT0S{`i4TyZjb3_QIEo;G{?ldWE z!&(rVdR>=Lx&M>nl$ z$t$&yAfc|Ho*3o9mvedG>%``cCh8LFpA(o$H<_cK=iNKcBCRn(1h!07@=2ucA*|Sy zxRu={3I#ZJg(g6h0k#gwO5ygBfcX+yiBkS48k(~YF@FbGk)N6(S-yW+I_)!2N6zsmU8V0}bPmE2HkaVQL9#tgXaCCKeG~+sajWsdxbLFP0%&A5BDR zsXy#o@?uUWy=#;wH0mO2l9gTdMMSkDk*{i3Hv%IhU_4G)?J&}?7~mA>(nt(qokP$y zzjW(L7Vxx7F1%z6*P+9k$A4J39H!=I&dn!6yWs!GSH{r9LRa;!TBZ_4m?Bk|jM-M3 z0^sTLq4T+$$z6Q<7!f!=Ej;+PjKEDts{L#~LEobU7-~$$7CsDEbyJ@o-}14-S{F-> z7*Dp!VR6lwXQ@yEgv=VPT&O9R4_%QAZHJ}wDP4(}as%x7E=gmYP;pf>L{i~kx7Q`( zJ)G^1-e9f9`OqbTUk>t`-(lA92pfFIyb2!{QXTrL);&9h)Fk?o>$XUGXm4)?SVP21 zvrkNj6VZU<&?6T^lb)mm z=S8=@zk` zeSILzvW9nI$JL%E?=AJNKb5|?>z4b|563?@PkLV5^PuPH99g739C~!`G0f91k3P3} z>`t~cNPYJ6>*t$*%yjH)qfw;#yb$YEo7`lCZb1X;(n`Mm5=cD1p~qCwK-YJ844d+O zV=IP4;kkH(zrjZtKN|7JUuu`;9oLboq3inv+wf(N&UadC#fKsj+xjlK?MDm?WnDiY zrrkU>L7gcj^ieH3mTf3qV}!v^5egscJ;3k!fi0o|5ccHGj5w)y)(> z=zaN3sbZlGlvI1ZZzF&I;S_bTdxrL&cqx5%o9LHB>x4m$E5mbA5ekzH|7fs$2&Po4+bO(~A;VK7hCP_j}@=dcgZg_pgu#S^kwhVbt?XzT58A!v3W6%%Mp=E6`R}rV z!G9%=-(wnrO!{A_+5cND-BFiTsPphx`5Est4O_XH;DTE{r%jPv{2BSN^oqqXjp1Re z9}LCD+y4c{HruBrAPm(<1+=6r{TL~6N#+lb*P5J~qhoBH=y~02QDlgW6B>#Q7%e_D z;yPGRbN4SyHpVYmV@byA=Z*ER)M*a~1c=Y!zIToOZmegNo_e<%eE0YdnD#@-*SKBK z$h~`npcd(&wr>9x`BQUme$#$@3YwCtJT%wluAHe^^cOlV?7-ZJy!CFfD%s@eKWgdu zs|?{SV$O-%q|3rlNB~b2FL#v7A6LV!A2Sp&NlD6 zi#%=#8I(v3^@v-YA-SE}YaPoLw`USgNj%Sy$rfq-^3ZIz3v5t6@CXf~vDdHgZ4E;J zKKyX=@BV=MrSGPE9mVaY$$Y1fS#bR?J~l_SFrX+#OO)!`@s7$3Mns#FTwcA3K4PS#f3xAIF_0FL z*bSjBm@FLWvq#s(In|)kGq%)FYlXr1FHc@#xs7=+aMVI!#*c*R2z0>CqA4zN=WzOt z^i*GqPL;?kpFDY!w?x$bTi2nQ7!H?Hlg>XO)6Lxf_aM#istmLEt47Iex!YeODr% ztR=ti?S~%9Ef1oCVhAL}Sp<r*W5mpn71f7H2U{)fra%?`zL z+5bu~9Gib7c-C;mRaaF_=&Ii($f7Ft3hU^;SWqN!)!6?I#n@PD&j`P4nav~Zx(`{@ zyS3z9K=+7*w3BbcoY)RwaWBicG;+4L?)s+n&;H!>7wU)o5qWzjp#|qt?`t^Z5twJ* z8+dMi(T&sRK}z&N1)k7#OY|eI|D;Ax|gkEomc`I`mRm<5f-e z-c2ybx;DA-9$J9*hKu9_vlocXU8Eww5vZWj#s=@ks2D$XO_L)YPb}BAkYw(eJxb7J zNRsoSN|G4Hl~+`7GZB%hu!=8Uq_?sz#(^~|f0og4v5AV2JO{IVc3G%J=4G<*WxYGd zaP$8yP@#=aM?CHN)Y_D(ghzb4k_yt68x9ioJf3a~K3%vy+bAKWtDNA*3b`G^^@h zyKm+G?S;wE`@wCRZB_R`3cDk&C3#b@e>5)J#Juq4F<<*ZxrR_Nc57qR4$I0r#5(=b zyw{TOb>`zCGnsDPks52M{JZtq=Z%j|^vPylM=x}h{(zTD*S^y9KNQt|ryPV2TsG2i zccXF2$ib>n*L#-~8Tw8^X*SzW27l9?%=2+e3|gItZVK;-!?l68X%`xw^Y*T!p^*~$ zDU<+%{OXC{B5WJYb5zep#QaUY zsGfKw3}#(IROw}BC@9E`xio-?0BZer%M*FpF5t0WWW%vQ`jzadm-L95K^P8HqY9{` ztW9*M@dg7)znw7d&XspknT8f^c>9r}+xzZ55f-odtaCYd_3Il|OFi~yJ@2>DOqZiB zsgMrupiRW?f#4Tt-$QC>=>`7UvesCxH{C1rWxjm%-0_ne_9AHa-P3o2uIv`e24_-e z4fF`FS)S3U7=GMYM5m69`lv_7UC&drn2c+eL*Pp#mABgG8PmBnZ{Dw&3wWGg{->TI z5-Z=!WMWMwZDD3wpWs5oDUgb~VLEloH?d=#P71-1w;>gOR;JnH{HKtV_)ZPN*i7dV zT34MnsaNo9^MzFQEp=Shuft`e_=sd6X%QS&^YMmmx1bqOviWlIZm;VMvpL3U~iyF!arWmB>zJfnD1I2z2fUYS+Az9ZSk<1<=8!4 zD{G4+L#Wb)qp4tE0IN-(T;K5zU7&|#w8*`Y(xz^^wzHfT5!0~_d0M5D_qVI0zK2Qx zgV;r3K6h(wSNff6S(KLWCmfGt&Bt7hEi{wc`-H}gj)_$|ZE^ic?d!kNSiOLhtQb96 zIe+Vd&iHVF%tPKFng&_Ttz2+emA`lHqc!18S`!yl20TMj(u;669CjcqL7oeLZ3)f} zALd7|4@eXOAPBXAjsz;r;6ZB~hZ;A$X)G1xI{gHS{xsCZK!+QYI&PhUeI`#~pGk)H zg~S1BO|85n+X7wdKuZ>vD9I>$id*p{0-6fc50b=&;1BV5zHHx7MF~;XJq*?!R1zL~ zX z@#$EA><}n~?b!BClVutCM0av8ruXa zd*p>w1ty+tRpC%L5=-wahBgg#)($tU&RGWClmCtG5lO0_935slGALL#R$RNUcFLA7 zR!v<5#qGlZ+ke9sQ4B=X^J7fEV+CK{7HAI-eI8QTIFnJ0yW@koDE8mTqe^LJZ{;x**Zt`+y#-?t4uw2%B zcxLT=3Ce-b1+hQhK1Z;$2Y;+K8d3_mcRWWWOp138sd$NXBJ#W=<`Is%WN@rGr3gM} zgwNJywbxqw$c?d&;`nJOv-2BRuxbP-(-=t3t~`TwU+RVrQ~_M>5!Evj%4PrWJZ%pweE+v_5EeNZn~{dpNNRjz}RSd+zt!La=WDWS_? zFsz-owzlhSxA3$6u%ZMO{N484lOOi4^XcDyzkk0if*cSl2bQ!Sg0$WKg#??&f^-pu zJOx$lPNA%u0=>DvZjl7LET_0iC%sRHy1hEW{CJDkDAE>kv=h zE9^>IgqU%FlbZ`lTt$2S&T%|?r;34)BNGV*t_(f6L?f%6}+)ea3b{U6zA{PZ)n5(xri!vbngDuf_jVt1`4 z7?e0U?Oq}8@JMnZW0}Yao^N)65XusD+nJ~XHjU?K|9D|h;&80lAi`-KIGqyw3nMi8 zZkEU}$4v2ey!<6ALA&_yz$c7RjD87k2y?j3I-ifZ!Fh26VNu#?MwYf=SF$3V zWW!(Vd|9FL$@ET0DJVYVPQ7BmQv{oCEv4gG>E1wg^}^-8bLiD1t`dsC-0Xbj!fT^& znq7qw6hgwJC*P!t+VSr^D^sdfF4NK)jtmIerQJaaPcO9m#Rl39J)`%Hnw(mAs*~SS z(1c2jbGSB(E|HV6Xk>F8>7anWgX^;x-Yg=Gs-NLz={T>-bJJ}~gfU9RGFtDnyMX|D zFY=^8z!GHm+nHO$i-8VZOd6+C&4>y(0WP&3i?>DNkBPbD%U0VnLd~LeCWqMi5jE>EV&@HcU7D!q| zCKItsnQEc&;{e}AxM_Kr?Ol*{nrEshyLZTpf$*TcPioZK;;*2CvlQ(Fi{|8T-X_-d zXd37-o}j6vav?sO0b4pgM$C4Bb@hZeq~sSDvnLKw88Tb8?1^)z zWre5l$GSzq$mK<ygIgk-1`Oz(h)Wjy${I9xvs0tl^9*zbp}|JDDAL#Uo?gE(+1 ziViK)_*`+}Auc&$ZkU!Jk$b{!-RqIv;^!`8V*DbK z+Q2~<5v;NnK7agC z%?e-IS`#%ZG1^)$J!mn@phGbMXLNEx1r}>!e`Ogzx8GTYlk!W>K}?-3)j>EyY^~Et zRY&Z;Hy3HGyQdOQ&)xoX1n9&mr`$gS`j>MiO6TU4ACl0dTHcq-#2(P`pa(&qq zsl$cun`Z+p9B#}%O%f51@80}L2mtXNDyw5!Q(HqcdY#&^M$OSJkc;t*mmbKR5n+xA zW;*o>nQ53J4RQvxI7@?iTR$_|tos`+bEb##D#^JQQAFaaZW`U!dB+xoWGpRelw%uS zfMREzqfn zn;~;--^R$~2=I%Q_b6zQ(Em7VLc3+R<}7=hNf$g7ED}L!lWq zq1(FM32p7c{q~{vbCvDuG;f<=f~<~N&T``}u>-mLn;xWi(h}z7_t-DYz34aE-7dF` zU+JYI3&khPt(Tg`f5HESSQJSvv7to{q-ICq5eLbo#gEG^8wZ!V>MQ|JR@Hf_^1?0J zj4x;zkS^bH2CK8^P>y7y@P?;n_s~no1q6 zI-V-A@Hs8N06W26S||9`G3UAaeT?hGkp}6QHppdJ9LGB(4i#C6BIwgH3J$0M(%a)C z_8CbUy;o*$s!cwKU^?#>n0BnZR-O^^X6@H?jv%>t1N2sikp~RWx-ieySHx1~T0368 z%#yrmz#|APq3L~@>fKU6?b|3#ySCU|>phv;jbj;_7?Q4)iom%Lz^`wt$lL69Pl?Gr z&WDrW_r)YQy)3ELlw`?O)3<$o+sNy7ZgXKWzEF^&iWyB;MvSX|yU(4Iq%2#ynHBpRbP9q8c!U*+z!BufNui0W6NWxnX1=DO+sP|o+fqdL-mM>+^KwTf?4P@J zAX3?UhQQCt&V5q#N+7N~vFbDeYLnS$?Qk*9F$=|cL0g}K+NLJju&AZ8+*6cyTtUIW zY07|Viu2PLhn|wEJf1&CfW7`^*sdzCoodOD#&iW$*Xx(*Ih0cAoreg6l>@2ATmIiSXWTaESDXk^ob2hb00Vm%Z4$)}^^>z$E!`sVa^gleO0^BDT6^EX8mc5K_P6nJtY(LtT3u-;GIAnSRsg*NfjCGm_KJYx~>qr^j7-7tEiV^>) zoS^N``gqs?G28!u=ps|DaR@a2GGsYViEPD&q}e=Xu@Ex~>-jPI6&#u|tiY7N@#Ups zp!n}xmof{|GHaGj3S>A$#gZ+m)f-?YcbtO*9c1Rm?xHQGIVL=cbNQJ*c~+q1D5|ti ziGE#GSyBDEy5`F3*XL?G7YyIjG~ACqSK~KQ{pLdR_)4b>>sf@D+VEL;OSDVx+}lgN zmT%tHi=B<$H4FxBd3SkuMknVTH`ygfv1MZ8`B96J(1P ztO5EBRQoa&bAz0hs_GjpvVzKs_ul$3-%I>bk@ZBG`5q@)ju?%L*~SXqxZ1^+T@tFq zikf-8dt*))L_|zfo397IHP^_Q6~9mnSZnp7FrV7^hN-g&M%NDQT{~iS$^YH#aS!z& zY0;L*TSTX|G8KJr23C<;J;0pP=9{|U*sIM-PJWwcd5cNSc#cNHdhA(GoLQ5@kgjs;n=v|^iye`r>YaHC6571$y=T_Z-@6@Nw?FD zZ#OjrsB-LE$_W7tmiOpqWbCZsR0`D1!)W zX4={}?lM`(eNsZluWpOeq=mo6NonHQt$YCPz?cae84YjR*q}@p8Lh0AgL6m(JvE3lB`)SKuo|Qvy&p}`q1Qqw)#LZ0kFcjYJC4(| zQeYC0IY>#aHn;OBmg{yae=Fe5srS1F)Ui6wJR7iK;+8k;&BMDEpA)pN-y<^VIG9b` zMpKRm_1n11V|2!89hx%JWpLJb@RXr)5=@H@u5H6HL8em=Zb$!%b^VO`{>p(S!N(SxBopC~B>gTRjA;C()G4}CHRqGTPR_%`Q| z#J8UoiE55_Ywopg0rE?vpwEx1VMH+e5zm8ETC(_EZU`W&VR+3elV z|0u1UUa{JLEyW(t23ET0tGtye2$s36{=~)bgIH`7SIi~Dm4*x6h|HH*d&ewkF0fd zo-JJPLehn)DT<|1LKww!1290@iP0(z_)nf|nko&Dg+xW)uEr3ooW-3ZMb zbPk0zylq!I@R7DMA3GG*z%zscufcR)r*$t!+RMOL_ohK9*6I@a?yd@L=i#=`4b)UW zQXfA2g!ZP$t|ISG0yK+}0#AsN|EO@}lVe(ayLYw$4WmZ-lhndThnv$_NC;7stYhWZ zq2@b&Yjn>^~v?HyhJ!wDG@Qs!#I#l-Jch7OdplW5w-##u{PWDheL$< zJ>A+xq|W0(yn(Y%M%-~fD|j4cv*I}?>6)MUGRiv0$M^FxrZ#)yj7dJd$7g zGqOh!$xmUc%?ecBVCjGSx329!s5$?AR{lSfR`f9$@Oma)73{bZ+j-o{<+0EcCv$${ z#UW?JHFTGp-I?ov_bbBUn`Q4yG(w>$KwpSfK^lcEEP2*M-@4TM&B_m+hnx4NtvZ~d zl(j}+w5IymniNOzVOy8`w`ZUJx?plp8wAgixBXhNn@@YNN8L1PjM_-}#coV|lqwxd zlso7dD^Oua=bgPptw#gwom<%kWdUwXSXdIJD0-OnHJj!ei0* z?~SH~;H_`lTCw^y)0Zp%Kla}9t*QO(_MHhcA%O&D=$+6zQl;xmp(6$aL`4Zj1t|tW zz@9)T(hNmGLgTB?A;s=X+0}mle{`bEpEjbXlG~9>0~YGDd_ly57^%ER{%0`~kaX^+4+Mw? z)o2F%B}THQ<0u6Ye~OW#)CDmzVlyvBq*5QDAXs(jIUUKRzkG(}tendPZbeL@rbJ*F z$m$N>WlQ178KPk}ynm7efX!ZmZs>Qb2JsdusxZdVamT0o`WtiO+!lJm##GoA0!sTc%kYY;nuaQ_ zC3T-W|70dNz&CzPd1;_ZYQH>Qe`L`MW-~X(c zJT9#E&!(+;W&z+qP0t~Pjfv{O|1fP0-Kmy%-XVkF>Cs5Uv^5~dq4y%DZMz1_{$^MC+zQB+L`7p}6((zk&#qgaRTp{3P`o}u{v1wI2Nxj6!t{B~@3NDn=fgkjCOc8& z>{eKEx+H9nAXWYMl2fWW0(NS#q?%!tmYLYFPQSRr+^j3nf~<%JM;VsK7FI+SHxRJX zMIS|Q_g(+HJWAWcB?ABwZG=n zUg;5Fi8U4MH+k^JhN?N$cUbGK%`?3*cKQ6bQbE3CyExqd>zp$gZxn0f#` zmzH}>y@B`Q@Nz#(qT~*Tl11tCW`L^wq+Tn}{tav5g$G}1i1z$*6<7en$-^V7M%f3x zz8v@7*^ZB~mBKHcCcNJU46r&{;6w5%Pu$Th_hmQCV~;bl6>nSC=cZo0$w=3m4DP76Ky*{tQ{@h1T4~yNIXEIO<+**{g-xK84pLS+R4dmsn2G0=t-a{q3*Z0! z75bm)bNu%t!*Tt{rq>U}gxy-HXU^Z#`Zl4q`(1k6=|y&C^F^f-21rpUL{_8B7nM?_ z?I~y(>fO2YIHcA2?Ua+}h||2Q;c()}b;jukC5r^{@XjMkTnheZ-wah~A*6%?yK~v9vG4c>4x-hnQfp*BEEFWj}8SOo=Tn zl|rxo%79Q@CBsWnSN)Lf60a7NG>5Dxz~nk{A8~(tN9%RZ{o$vtUEF46)*8cM(Ds5` zGiKX&b+S^#WmbBbcPMOB86YSrBYP%)_NY0<6$d&4%;*M;eDSZ5EkH3z=|hjzaP)a+ zU8v~8P2wj5-fa|1GfMz}h(>&lBb<-a4Mmzf!)#|&eyE6#D%&Vvk?<9|lOv|PN(lxv%iI zFwGt3XEo}}I=sQic!@@%n01TYweWtCe}m9O1D{>M9 z;ASW1nk$DRl#zG4#r<62qc*AQGT|LVpmlMCFihilU?~qYi*^;wXn$o*wn1R{)rw(Hej;-T{aj+5f6vJcuv0l`s%xi!x2TuQF|^cXwAR- zr~l=<@;}29kP^Yz3OG4@j}wL@4-ykIdAnj|iow^`s1{Br?NpPYG^@MVZ|&jdO{;4r zIMZ0E9q*eLJNIH9L7HxNFUCE!z-1kNXl#?6*M0Pq%*^WE8uE zVPX5h$3}7rylcCr#QKgoo_;m8E$+)#_3wTYt65t(XGXTH*{!f}#(vdWE7FV3%_G9DjK@PAeh7_sRu{%x={%Y(Q-`ocm!Z5 zIXNYVx?=pk-)gwd7mq@Q2&CP#;`~kgE+w|0isjoVfQ6A59D+r^gDPwNNMTo?D?$TB z%_{DR)Smk^#LRGdCxh||X}TXO?w6%sYgXj(Fj82K4DO)_%aNB^>TdsCj{H}5%w^Fe z#6SJ3J4QWx#(PBJGD)2K(unfRarG`6q+We&(If3RZ3ucnd@gHqlw*2%^xzr#rI$$` zYzp9F-)9OI=oprW5_U-r_;3Zv3?6rU-p1%pHb@fA@U2Wvk-Yb?a0&bm{ULpd7HsD_33cd*$DG` z1Mj$dRw`2X+7RS-QwwlKb%Q zg5=*N}VZ61H73TsDlRtFv@j80pO5?yveP6>Zr=&$^ zd=Z+fZH;)P+ugow1)Ozw)Z1%)24iop0}s32^1)M9v{qbFVn-W69fgr0tZXoo#S6_u z=0S+vrQjLfhM0-bR7EAa1)~yO8ML|?jgkd?L4(#U-H0F=R$ROYDVW+=64!Mbo+D&p z>uOT63|*98djKb!2S469lTbR`4;!F9z$;(WeY)ovVh+hMtX_RhMdsBz2Ng0xO@KkwBb2ss0Q6=L(#2_^> zD<+ffb55LYiuG(1v=Ke4EpiOwd$2O1;iKpFFF)9AdU@v1K4SA2S8o^@l^o+GY$ut8 zxF9`p$}-?h#M4JJ{WfQlUNfJ(v5Q;z$U@T8s=CeF5?17RIbIBM$^9@{yxj6*pxfEj z7ndIKY^|oPZg6ZLD7=H!Uob_en4TJGcHVRG3gC6xl26aPZCrRLQD2J0x>1ho*eoVM zsZt_{HHNBUWf+~a;!A9YR$yjEzmI$=Z9dG#2}&{?X5JA4<5E=X#$b%aRQC2=EX}Qj zYpf5LwE$&ozaMQbR&(h{j5)@e@Up#4AdzWxXXhlC54qZ~Im9jXRT!x;u0wPKM@*(q z4#ndbHKNLM$>D>{Oo|{A$hn<^f{()YBJ7IF=w!NAFFw42!0}^Z7k3OfmgRT5 zRZ1r9zCs`uGjih_$5DEMblV;>XE9qu#g|F38sgxNg&$K^u?thO>%CY|KcR@;hBca$ z1J)ObDxL0szH(hlN8q)tUCfi|hDD^~)%9a??@GG9WL9z&xY_$lhN_j4SS@nK>`)Av*V zxti;Kbu3wD?}rf4ygrj8a7+X*6pdzS2M1+#dD3f)m(dC@*1w9Sj-W`}0j?yg0+YPP z-S@?|mLlMplg+mt*Vy-JJC3DfQsKMFBzv=?|A1|iGdniv@)}8Q z1(ts0G>fEVuLx?aZGV(az?~1OZDzKJp;0)^g3;+|9o+YGvFi$WTp(fivU^|()O=&D zs(`=g0dnj8?AURu3rVK4<8QXV@C=4?pIz#*;s8WxIWSroH`_=NNiThJK`G zCfhQKIRXB^_tgJq2-*L0qc}H@i^=Q#O!ifOx>Lsk*!9u6Ctt*%TLa~ta)G!+IO!I< z?A5vjtMVcT{tV@uc%Hft%6YWy#XqgDgrS^%gQ1{IzuKuSWVcTdYw`;eQ^l=D1w05< z6t27u!iRt)zf~IT`5E3GGTGj>)5@g!&vRqbNEBy#fjH)ZAm#Lx&(p8Y-iT>H*3alD zVHD?;oL-+Wii25(dQGcQ>BH-XK1@92(R3%>$n@ZfB4s}Epl6>^5QLfwJ@p?m zMf0BerqC1*!>^xTy}kZ)Kpa_a$vZ0QVv*%m8&jiSxZEl$;7bX=M`9ZNi@L%+THCxw zlCL0&vG#tpLk`PPPj1Fu{N$B+#W*BXj0bA#J}fg9=Vs~Gkbi8s@*-DTQ0Zrvo&zh7 z@@d6SW{jaj&qJu8ZT1>Ai*Z+02HcpSHMiDhR0hIVB1rilOY1Z;=0A1W&x zSamO7hN)kSQCR&mx%`lp1+w89yl3+8QD4=sGYhevgEn`k_rXOo!dTDPnaPGnhrcu& z$o{?NnnBiF;Zwdt&v@-MKd`2+t|l3rf*U|HkJ$#*0^Iw4c_h$t({<6+Q}cnIX79HF zpG5~D`15q+qHFHR!fSW%^CQ=T{yz+w|3h|O|2L@f-wo=#D6YcapXe)I{11CexHR+F zfCRKf;O!fMPi#a;;?^Uxs8yEIkgZtAT)b<@nNYB!g1HdXc`^Op4(gQNTfIU9z4rNf z$}W&y)jW7mhkQA%VFOQ81Db3jpmGG$gyk|PxaXt{9HuG&*ZFN)<;#l+!iM+R$6yM< zLAC>ZhUEET;IiVJHT5bcZZvM$!Ey7}9!i=OkMODr*?Gyl#>e}(*1#DUqdMds!@obs z=QcW$;!Jt*U1(rs_*3yB`mzYVO|CjF|fCc=`^$L6~8C8eGoL}5wky0Xaw zNL225jQK63Rxl&Ba40?dvH733{q+O*#=M}`i^r1)Hk6a2FY?D)!{+djKj4BwwY#J8%?l!j3RGV_ocS$f&jsp{4`ADpQ@>K!>{o6;)0pDzgbv&q z#$kb{A#g7PM& z?lJ219?~kzji&2Pzf`cTYUR1%-qC6^1#g0qo#SMqkrq>D`THv}X*ut5HopVVf}XC$ z<)`OM;a5L*X|BLgP$N>rS3RzkV5R@AAp%*%y@LI3(ZK&rtgZi+BKSWl?y`gQVU#k0 zOF)QXP~tK7f8=)qppMa;MM_rugLLHNo)Y&w^8{l;hJNda(Qn^s@3!Q7KpPzIrva_w z$|04b#oplK;kaS z1B7Y0V^qLZ1-1Dysn-apU|}G(_v%0wWI`1s_~FsP9UJ25ll~#C1e^YQNFrvOg(!^U z9=rKEIfY%)Ni%Vb72Kbl-Czg_=s<@QWWguv%HB!2^sw6T$D82y!t?!tUO6cZ8b+mz z$Evj#)*=($IYNe}HY=xzyR6#wk{)Uz*IuqhSDE5#sgT132rmT6AR=SRsGvzzoYP;;WUY*zaLDdH+))I5Dtzr#quAnnwr39~6mU4Ss9LA0yHSp%Ad5noQOp(2=2XT+5KpS4lqDwx+O$n(=GGbC5 z+^L2q>{g2dFEeHM{4!%UMSM@2Dh-G`r&pktW$H*EMBNe5X*L)Cg>H|^B)gE=6#N!0 zPVt6*>iOg}!`$es6^73`(&-ZfRC1Xr=fmZcnE^|a=2i)GJ*WtpV2$~jt}+?<&fYnO z4%>3Q7E?XRSJD~3c4Ug%v{YQXFKSJXUF$ynRb>zFb@k_m9{S z2EYMp(ErtgDF45fVEF~w-TQ6v!mIqJUTP+PUFEC)%T@j%Y>?} z2ob5&UKTf`b^js@>4|?;FIo&VqZL|O{a(0SVvD>QeYqb?Y)E-pCB}F22mF}cKfUJh zj1mY42`VV-DJY?7?Wc@9^;Gh6ijrQ|`1|zUJf0cMac1R(AW4R|fGIAeIc`{SCV$T! zqua?x6ZRFWC}6(Iv3uVa&7h-k&_42Is%$c0uF{}>(ZR9dJEww!87Z?GkslT-;Y+Xn z;>LL1;c?0hjfTJYb8!^pyxu+8Lb0)f0fJ7WtDPd;X40 zCGX5!By{i(&NJ)b&R%DQ5Vuzpc$y9p~QM=OBzTri|WBSQt-N>xU;XgWh4pic;JwH}_J&2J(j6+aH`BO&SP$lrmc z){Vc@)db-t(yBJxw_s@CVc`$e)dDAykjT6;P=iL0AZGDo;l{iBbI>;}1$d{jIWr-R zS>OXqS`R&PelRkTy*U>gQ;e#w)E^*Eq4I!>zYAHvz*x7*h4_xgWr^b3A2?ci zY_g_(=kZV4w;a{8#<|j2c*6rRV)T<`=+{hX@c<|W31H*=V*v|>k9m;EjLHMN?d;;M z5o(c-K8%DP;&y}UPvj0twKtwZU`Z!dVr0?&arqdOP|_uK&%L&x1+^TyV9BM7xnHTH*0CLzgs=b?^N5;mZp`ZVLSaYb*B! zPnSWtb;8eG6*Uov$xz9?C}WWH(|@0~7O4j+ld)9Pr;%k#I3qZ>w*5O%TTy)a4A{o+ zC;~[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/ - const userConfig = ${JSON.stringify(config || {})}; + const userConfig = ${JSON.stringify(config?.rrwebPlayer || {})}; window.replayer = new rrwebPlayer({ target: document.body, + width: userConfig.width, + height: userConfig.height, props: { ...userConfig, events, - showController: false, - autoPlay: false, // autoPlay off by default + showController: false, }, }); window.replayer.addEventListener('finish', () => window.onReplayFinish()); + window.replayer.addEventListener('ui-update-progress', (payload)=> window.onReplayProgressUpdate + (payload)); + window.replayer.addEventListener('resize',()=>document.querySelector('.replayer-wrapper').style.transform = 'scale(${ + (config?.resolutionRatio ?? 1) * MaxScaleValue + }) translate(-50%, -50%)'); `; } -export class RRvideo { - private browser!: Browser; - private page!: Page; - private state: 'idle' | 'recording' | 'closed' = 'idle'; - private config = { - ...defaultConfig, +/** + * Preprocess all events to get a maximum view port size. + */ +function getMaxViewport(events: eventWithTime[]) { + let maxWidth = 0, + maxHeight = 0; + events.forEach((event) => { + if (event.type !== EventType.Meta) return; + if (event.data.width > maxWidth) maxWidth = event.data.width; + if (event.data.height > maxHeight) maxHeight = event.data.height; + }); + return { + width: maxWidth, + height: maxHeight, }; - - constructor(config: RRvideoConfig) { - this.updateConfig(config); - } - - public async transform() { - try { - this.browser = await puppeteer.launch({ - headless: this.config.headless, - }); - this.page = await this.browser.newPage(); - await this.page.goto('about:blank'); - - await this.page.exposeFunction('onReplayFinish', () => { - void this.finishRecording(); - }); - - const eventsPath = path.isAbsolute(this.config.input) - ? this.config.input - : path.resolve(process.cwd(), this.config.input); - const events = JSON.parse( - fs.readFileSync(eventsPath, 'utf-8'), - ) as eventWithTime[]; - - await this.page.setContent(getHtml(events, this.config.rrwebPlayer)); - - setTimeout(() => { - void this.startRecording().then(() => { - return this.page.evaluate('window.replayer.play();'); - }); - }, this.config.startDelayTime); - } catch (error) { - this.config.cb('', error as Error); - } - } - - public updateConfig(config: RRvideoConfig) { - if (!config.input) throw new Error('input is required'); - config.output = config.output || defaultConfig.output; - Object.assign(this.config, defaultConfig, config); - } - - private async startRecording() { - this.state = 'recording'; - let wrapperSelector = '.replayer-wrapper'; - if (this.config.rrwebPlayer.width && this.config.rrwebPlayer.height) { - wrapperSelector = '.rr-player'; - } - const wrapperEl = await this.page.$(wrapperSelector); - - if (!wrapperEl) { - throw new Error('failed to get replayer element'); - } - - // start ffmpeg - const args = [ - // fps - '-framerate', - this.config.fps.toString(), - // input - '-f', - 'image2pipe', - '-i', - '-', - // output - '-y', - this.config.output, - ]; - - const ffmpegProcess = spawn('ffmpeg', args); - ffmpegProcess.stderr.setEncoding('utf-8'); - ffmpegProcess.stderr.on('data', console.log); - - let processError: Error | null = null; - - const timer = setInterval(() => { - if (this.state === 'recording' && !processError) { - void wrapperEl - .screenshot({ - encoding: 'binary', - }) - .then((buffer) => ffmpegProcess.stdin.write(buffer)) - .catch(); - } else { - clearInterval(timer); - if (this.state === 'closed' && !processError) { - ffmpegProcess.stdin.end(); - } - } - }, 1000 / this.config.fps); - - const outputPath = path.isAbsolute(this.config.output) - ? this.config.output - : path.resolve(process.cwd(), this.config.output); - ffmpegProcess.on('close', () => { - if (processError) { - return; - } - this.config.cb(outputPath, null); - }); - ffmpegProcess.on('error', (error) => { - if (processError) { - return; - } - processError = error; - this.config.cb(outputPath, error); - }); - ffmpegProcess.stdin.on('error', (error) => { - if (processError) { - return; - } - processError = error; - this.config.cb(outputPath, error); - }); - } - - private async finishRecording() { - this.state = 'closed'; - await this.browser.close(); - } } -export function transformToVideo(config: RRvideoConfig): Promise { - return new Promise((resolve, reject) => { - const rrvideo = new RRvideo({ - ...config, - cb(file, error) { - if (error) { - return reject(error); - } - resolve(file); - }, - }); - void rrvideo.transform(); +export async function transformToVideo(options: RRvideoConfig) { + const defaultVideoDir = '__rrvideo__temp__'; + const config = { ...defaultConfig }; + if (!options.input) throw new Error('input is required'); + // If the output is not specified or undefined, use the default value. + if (!options.output) delete options.output; + Object.assign(config, options); + if (config.resolutionRatio > 1) config.resolutionRatio = 1; // The max value is 1. + + const eventsPath = path.isAbsolute(config.input) + ? config.input + : path.resolve(process.cwd(), config.input); + const outputPath = path.isAbsolute(config.output) + ? config.output + : path.resolve(process.cwd(), config.output); + const events = JSON.parse( + fs.readFileSync(eventsPath, 'utf-8'), + ) as eventWithTime[]; + + // Make the browser viewport fit the player size. + const maxViewport = getMaxViewport(events); + // Use the scaling method to improve the video quality. + const scaledViewport = { + width: Math.round( + maxViewport.width * (config.resolutionRatio ?? 1) * MaxScaleValue, + ), + height: Math.round( + maxViewport.height * (config.resolutionRatio ?? 1) * MaxScaleValue, + ), + }; + Object.assign(config.rrwebPlayer, scaledViewport); + const browser = await chromium.launch({ + headless: config.headless, }); + const context = await browser.newContext({ + viewport: scaledViewport, + recordVideo: { + dir: defaultVideoDir, + size: scaledViewport, + }, + }); + const page = await context.newPage(); + await page.goto('about:blank'); + await page.exposeFunction( + 'onReplayProgressUpdate', + (data: { payload: number }) => { + config.onProgressUpdate(data.payload); + }, + ); + + // Wait for the replay to finish + await new Promise( + (resolve) => + void page + .exposeFunction('onReplayFinish', () => resolve()) + .then(() => page.setContent(getHtml(events, config))), + ); + const videoPath = (await page.video()?.path()) || ''; + const cleanFiles = async (videoPath: string) => { + await fs.remove(videoPath); + if ((await fs.readdir(defaultVideoDir)).length === 0) { + await fs.remove(defaultVideoDir); + } + }; + await context.close(); + await Promise.all([ + fs + .move(videoPath, outputPath, { overwrite: true }) + .catch((e) => { + console.error( + "Can't create video file. Please check the output path.", + e, + ); + }) + .finally(() => void cleanFiles(videoPath)), + browser.close(), + ]); + return outputPath; } diff --git a/packages/rrvideo/test/cli.test.ts b/packages/rrvideo/test/cli.test.ts new file mode 100644 index 0000000000..29efe4815c --- /dev/null +++ b/packages/rrvideo/test/cli.test.ts @@ -0,0 +1,45 @@ +import { execSync } from 'child_process'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import exampleEvents from './events/example'; + +describe('should be able to run cli', () => { + beforeAll(() => { + fs.mkdirSync(path.resolve(__dirname, './generated')); + fs.writeJsonSync( + path.resolve(__dirname, './generated/example.json'), + exampleEvents, + { + spaces: 2, + }, + ); + }); + afterAll(async () => { + await fs.remove(path.resolve(__dirname, './generated')); + }); + + it('should throw error without input path', () => { + expect(() => { + execSync('node ./build/cli.js', { stdio: 'pipe' }); + }).toThrowError(/.*please pass --input to your rrweb events file.*/); + }); + + it('should generate a video without output path', () => { + execSync('node ./build/cli.js --input ./test/generated/example.json', { + stdio: 'pipe', + }); + const outputFile = path.resolve(__dirname, '../rrvideo-output.webm'); + expect(fs.existsSync(outputFile)).toBe(true); + fs.removeSync(outputFile); + }); + + it('should generate a video with specific output path', () => { + const outputFile = path.resolve(__dirname, './generated/output.webm'); + execSync( + `node ./build/cli.js --input ./test/generated/example.json --output ${outputFile}`, + { stdio: 'pipe' }, + ); + expect(fs.existsSync(outputFile)).toBe(true); + fs.removeSync(outputFile); + }); +}); diff --git a/packages/rrvideo/test/events/example.ts b/packages/rrvideo/test/events/example.ts new file mode 100644 index 0000000000..00701fd6ca --- /dev/null +++ b/packages/rrvideo/test/events/example.ts @@ -0,0 +1,147 @@ +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: 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: [], + }, + ], + }, + ], + }, + 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', + childNodes: [], + attributes: {}, + id: 26, + }, + }, + { + parentId: 26, + nextId: null, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueC' }, + childNodes: [], + id: 27, + }, + }, + { + parentId: 27, + nextId: null, + node: { type: 3, textContent: 'C', id: 28 }, + }, + { + parentId: 26, + nextId: 27, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueB', selected: true }, + childNodes: [], + id: 29, + }, + }, + { + parentId: 26, + nextId: 29, + node: { + type: 2, + tagName: 'option', + attributes: { value: 'valueA' }, + childNodes: [], + id: 30, + }, + }, + { + parentId: 30, + nextId: null, + node: { type: 3, textContent: 'A', id: 31 }, + }, + { + parentId: 29, + nextId: null, + node: { type: 3, textContent: 'B', id: 32 }, + }, + ], + }, + timestamp: now + 200, + }, + // input event + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Input, + text: 'valueA', + isChecked: false, + id: 26, + }, + timestamp: now + 300, + }, +]; + +export default events; diff --git a/packages/rrvideo/test/tsconfig.json b/packages/rrvideo/test/tsconfig.json new file mode 100644 index 0000000000..875cb60012 --- /dev/null +++ b/packages/rrvideo/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "compilerOptions": {} +} diff --git a/packages/rrvideo/tsconfig.json b/packages/rrvideo/tsconfig.json index ea6d53e2ca..95955dda4a 100644 --- a/packages/rrvideo/tsconfig.json +++ b/packages/rrvideo/tsconfig.json @@ -16,6 +16,7 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, + "exclude": ["build", "node_modules", "test"], "references": [ { "path": "../rrweb-player" diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 9a3db36a3b..e4d8f3b766 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -44,7 +44,7 @@ "@types/chai": "^4.1.4", "@types/jest": "^27.0.2", "@types/jsdom": "^20.0.0", - "@types/node": "^10.11.3", + "@types/node": "^18.15.11", "@types/puppeteer": "^1.12.4", "cross-env": "^5.2.0", "jest": "^27.2.4", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 474b4bd198..9064f03e93 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -51,7 +51,7 @@ "@types/inquirer": "^8.2.1", "@types/jest": "^27.4.1", "@types/jest-image-snapshot": "^5.1.0", - "@types/node": "^17.0.21", + "@types/node": "^18.15.11", "@types/offscreencanvas": "^2019.6.4", "@types/puppeteer": "^5.4.4", "construct-style-sheets-polyfill": "^3.1.0", 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, }, diff --git a/yarn.lock b/yarn.lock index 2ecefecc30..a3e1e00c82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2517,6 +2517,24 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@open-tech-world/cli-progress-bar@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@open-tech-world/cli-progress-bar/-/cli-progress-bar-2.0.2.tgz#9a4f3470ee460224e7a38790d0d3f257fe9d6812" + integrity sha512-miNmdKNdKp7Lhy295wxnJcWFrbIAoQmypoCynlG8HTQPxsG5dhOpPL5udsZFoW99RxcLAj2JTCaEhrosTzPE3g== + dependencies: + "@open-tech-world/es-cli-styles" "^0.3.0" + "@open-tech-world/es-utils" "^0.10.0" + +"@open-tech-world/es-cli-styles@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-tech-world/es-cli-styles/-/es-cli-styles-0.3.0.tgz#316c3c2934ed87629366a5827bc2353fa4c061c7" + integrity sha512-0SyxBAUkUDkjt83snx1QvPDk6zJJIb/xRbY0daWGG6ONCfx5RITxc0qty2tLmyBNsCM41+V1shtHNeSrEBu8KQ== + +"@open-tech-world/es-utils@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@open-tech-world/es-utils/-/es-utils-0.10.0.tgz#97ca72c7dad63dcf9a3534b01f3d763f187bafb2" + integrity sha512-JqpxcVk3Go4RAArI3oB18fhIMeeuNSThwmSQM7ScygVv2/AHRAxkkiodtggC7QniJvLs7i5AzL9pILNvAEBYDg== + "@polka/url@^0.5.0": version "0.5.0" resolved "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz" @@ -2805,6 +2823,14 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/fs-extra@11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.1.tgz#f542ec47810532a8a252127e6e105f487e0a6ea5" + integrity sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" @@ -2885,6 +2911,13 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/jsonfile@*": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.1.tgz#ac84e9aefa74a2425a0fb3012bdea44f58970f1b" + integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png== + dependencies: + "@types/node" "*" + "@types/lodash.mergewith@4.6.6": version "4.6.6" resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10" @@ -2917,20 +2950,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.24.tgz#c37ac69cb2948afb4cef95f424fa0037971a9a5c" integrity sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ== -"@types/node@^10.11.3": - version "10.17.60" - resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== - "@types/node@^12.7.1": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== -"@types/node@^17.0.21": - version "17.0.21" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz" - integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== +"@types/node@^18.15.11": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -4292,13 +4320,6 @@ chrome-launcher@0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromium-bidi@0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.5.tgz#a352e755536dde609bd2c77e4b1f0906bff8784e" - integrity sha512-rkav9YzRfAshSTG3wNXF7P7yNiI29QAo1xBXElPoCoSQR5n20q3cOyVhDv6S7+GlF/CJ/emUxlQiR0xOPurkGg== - dependencies: - mitt "3.0.0" - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" @@ -4645,16 +4666,6 @@ core-util-is@^1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cosmiconfig@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.0.tgz#947e174c796483ccf0a48476c24e4fefb7e1aea8" - integrity sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg== - dependencies: - import-fresh "^3.2.1" - js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" @@ -5147,11 +5158,6 @@ devtools-protocol@0.0.1036444: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1036444.tgz#a570d3cdde61527c82f9b03919847b8ac7b1c2b9" integrity sha512-0y4f/T8H9lsESV9kKP1HDUXgHxCdniFeJh6Erq+FbdOEvp/Ydp9t8kcAAM5gOd17pMrTDlFWntoHtzzeTUWKNw== -devtools-protocol@0.0.1094867: - version "0.0.1094867" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz#2ab93908e9376bd85d4e0604aa2651258f13e374" - integrity sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ== - devtools-protocol@0.0.869402: version "0.0.869402" resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" @@ -6626,6 +6632,15 @@ fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -6873,16 +6888,6 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^9.2.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" - integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== - dependencies: - fs.realpath "^1.0.0" - minimatch "^7.4.1" - minipass "^4.2.4" - path-scurry "^1.6.1" - glob@~7.2.0: version "7.2.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.2.tgz#29deb38e1ef90f132d5958abe9c3ee8e87f3c318" @@ -9406,11 +9411,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.14.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - magic-string@^0.25.7: version "0.25.7" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" @@ -9700,13 +9700,6 @@ minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^7.4.1: - version "7.4.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" - integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== - dependencies: - brace-expansion "^2.0.1" - minimatch@~3.0.5: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" @@ -9728,12 +9721,7 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -minipass@^4.0.2, minipass@^4.2.4: - version "4.2.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" - integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== - -mitt@3.0.0, mitt@^3.0.0: +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== @@ -10371,14 +10359,6 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" - integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== - dependencies: - lru-cache "^7.14.1" - minipass "^4.0.2" - path-strip-sep@^1.0.10: version "1.0.10" resolved "https://registry.npmjs.org/path-strip-sep/-/path-strip-sep-1.0.10.tgz" @@ -10520,6 +10500,18 @@ pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^5.0.0" +playwright-core@1.32.1: + version "1.32.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.1.tgz#5a10c32403323b07d75ea428ebeed866a80b76a1" + integrity sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA== + +playwright@^1.32.1: + version "1.32.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.32.1.tgz#c48195850740fbdbd7702f37e5a891b13259f689" + integrity sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg== + dependencies: + playwright-core "1.32.1" + pngjs@^3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz" @@ -11081,23 +11073,6 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -puppeteer-core@19.7.5: - version "19.7.5" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.7.5.tgz#cedc8eb7862fe7a8aa2a25ed167c0f1230de72b2" - integrity sha512-EJuNha+SxPfaYFbkoWU80H3Wb1SiQH5fFyb2xdbWda0ziax5mhV63UMlqNfPeTDIWarwtR4OIcq/9VqY8HPOsg== - dependencies: - chromium-bidi "0.4.5" - cross-fetch "3.1.5" - debug "4.3.4" - devtools-protocol "0.0.1094867" - extract-zip "2.0.1" - https-proxy-agent "5.0.1" - proxy-from-env "1.1.0" - rimraf "4.4.0" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.12.1" - puppeteer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-11.0.0.tgz#0808719c38e15315ecc1b1c28911f1c9054d201f" @@ -11133,17 +11108,6 @@ puppeteer@^17.1.3: unbzip2-stream "1.4.3" ws "8.8.1" -puppeteer@^19.7.2: - version "19.7.5" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.7.5.tgz#d7db0dfcc80ca2cdf8eb0100bae1ce888a841389" - integrity sha512-UqD8K+yaZa6/hwzP54AATCiHrEYGGxzQcse9cZzrtsVGd8wT0llCdYhsBp8n+zvnb1ofY0YFgI3TYZ/MiX5uXQ== - dependencies: - cosmiconfig "8.1.0" - https-proxy-agent "5.0.1" - progress "2.0.3" - proxy-from-env "1.1.0" - puppeteer-core "19.7.5" - puppeteer@^9.1.1: version "9.1.1" resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" @@ -11647,13 +11611,6 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" - integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== - dependencies: - glob "^9.2.0" - rimraf@^2.6.2: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" @@ -13656,11 +13613,6 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.12.1: - version "8.12.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" - integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== - ws@8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" From 0fedb8ed19000bd714bb06c43cb2c3ba5682da29 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Mon, 1 May 2023 14:24:14 +0200 Subject: [PATCH 189/201] Docs: scale sponsors by donation size (#1217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Scale sponsors Gold sponsors where a bit too big. I've scaled down all sponsors to match their donation level (calculation below). Also makes the low res image of our gold sponsor less apparent. Example: Bronze Sponsors Calculation: 50 pixels per donation $ 100$ = 5000 pixels √5000 = +/-70 Resolution: 70*70 = +/-5000 pixels * Create fresh-cars-impress.md --- .changeset/fresh-cars-impress.md | 2 ++ README.md | 30 +++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 .changeset/fresh-cars-impress.md diff --git a/.changeset/fresh-cars-impress.md b/.changeset/fresh-cars-impress.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/fresh-cars-impress.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/README.md b/README.md index bf70a96b6a..69f6a9560b 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,13 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE

- - - +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor
@@ -91,8 +95,13 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE
- - +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor
@@ -100,8 +109,15 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE
- - +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor +sponsor
From 4dcdcf7ec448758a9f7e49df4a8e1d8e46f5ca43 Mon Sep 17 00:00:00 2001 From: Ben White Date: Mon, 15 May 2023 14:19:42 +0200 Subject: [PATCH 190/201] fix: Rrror parser throw (#1225) * Warn instead of throwing error when parsing for console logs * Added error to warning --- .../rrweb/src/plugins/console/record/error-stack-parser.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 4c0e49c309..b34487935e 100644 --- a/packages/rrweb/src/plugins/console/record/error-stack-parser.ts +++ b/packages/rrweb/src/plugins/console/record/error-stack-parser.ts @@ -71,7 +71,11 @@ export const ErrorStackParser = { } else if (error.stack) { return this.parseFFOrSafari(error as { stack: string }); } else { - throw new Error('Cannot parse given Error object'); + console.warn( + '[console-record-plugin]: Failed to parse error object:', + error, + ); + return []; } }, // Separate line and column numbers from a string of the form: (URI:Line:Column) From a1ec9a273e6634eec67098fdd880ee681648fbbd Mon Sep 17 00:00:00 2001 From: fukang wang Date: Mon, 22 May 2023 10:19:28 +0800 Subject: [PATCH 191/201] perf: optimize performance of the DoubleLinkedList get (#1220) * perf: optimize performance of the DoubleLinkedList get * fix: delete addedNodeIndexArr --- .changeset/gold-terms-look.md | 5 +++ packages/rrweb/src/record/mutation.ts | 14 ++++++- .../rrweb/test/benchmark/dom-mutation.test.ts | 6 +++ .../benchmark-dom-mutation-add-and-move.html | 37 +++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 .changeset/gold-terms-look.md create mode 100644 packages/rrweb/test/html/benchmark-dom-mutation-add-and-move.html diff --git a/.changeset/gold-terms-look.md b/.changeset/gold-terms-look.md new file mode 100644 index 0000000000..4ad333341c --- /dev/null +++ b/.changeset/gold-terms-look.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +perf: optimize performance of the DoubleLinkedList get diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index e0b03eb1dd..337394acf1 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -48,6 +48,7 @@ function isNodeInLinkedList(n: Node | NodeInLinkedList): n is NodeInLinkedList { class DoubleLinkedList { public length = 0; public head: DoubleLinkedListNode | null = null; + public tail: DoubleLinkedListNode | null = null; public get(position: number) { if (position >= this.length) { @@ -95,6 +96,9 @@ class DoubleLinkedList { node.next = this.head; this.head = node; } + if (node.next === null) { + this.tail = node; + } this.length++; } @@ -108,11 +112,15 @@ class DoubleLinkedList { this.head = current.next; if (this.head) { this.head.previous = null; + } else { + this.tail = null; } } else { current.previous.next = current.next; if (current.next) { current.next.previous = current.previous; + } else { + this.tail = current.previous; } } if (n.__ln) { @@ -368,8 +376,10 @@ export default class MutationBuffer { } } if (!node) { - for (let index = addList.length - 1; index >= 0; index--) { - const _node = addList.get(index); + let tailNode = addList.tail; + while (tailNode) { + const _node = tailNode; + tailNode = tailNode.previous; // ensure _node is defined before attempting to find value if (_node) { const parentId = this.mirror.getId(_node.value.parentNode); diff --git a/packages/rrweb/test/benchmark/dom-mutation.test.ts b/packages/rrweb/test/benchmark/dom-mutation.test.ts index 57050fb400..3da794db45 100644 --- a/packages/rrweb/test/benchmark/dom-mutation.test.ts +++ b/packages/rrweb/test/benchmark/dom-mutation.test.ts @@ -36,6 +36,12 @@ const suites: Array< eval: 'window.workload()', times: 5, }, + { + title: 'create 10000 DOM nodes and move it to new container', + html: 'benchmark-dom-mutation-add-and-move.html', + eval: 'window.workload()', + times: 5, + }, ]; function avg(v: number[]): number { diff --git a/packages/rrweb/test/html/benchmark-dom-mutation-add-and-move.html b/packages/rrweb/test/html/benchmark-dom-mutation-add-and-move.html new file mode 100644 index 0000000000..922b8c9ffc --- /dev/null +++ b/packages/rrweb/test/html/benchmark-dom-mutation-add-and-move.html @@ -0,0 +1,37 @@ + + +
+ + + From aca67a06cdf6577fafcc27357eb9503c4d344866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Stipi=C4=87?= Date: Mon, 22 May 2023 04:20:56 +0200 Subject: [PATCH 192/201] docs: add analyzee to readme (#1227) Added analyzee as one of the SaaS that use rrweb in one of their products --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 69f6a9560b..491b7fb217 100644 --- a/README.md +++ b/README.md @@ -213,5 +213,10 @@ In addition to adding integration tests and unit tests, rrweb also provides a RE The open source, fullstack Monitoring Platform. + + + Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions. + + From a6ce7182ce59da8a565b12f7dfcb87eb10ec7ca4 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Mon, 22 May 2023 03:53:36 +0100 Subject: [PATCH 193/201] Avoid triggering a CSP (content security policy) error (#846) * Fix for #816 - avoid triggering a CSP (content security policy) error with `.setAttribute('style')` * The bare unattachedDoc that I previously naively created didn't have a doctype and wasn't a HTML document, so the child style element didn't have the `old.style` attribute available * Add a try/catch to provide some robustness in case `document.implementation.createHTMLDocument` isn't available --- packages/rrweb/src/record/mutation.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 337394acf1..f352424b5f 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -472,6 +472,14 @@ export default class MutationBuffer { if (isIgnored(m.target, this.mirror)) { return; } + let unattachedDoc; + try { + // avoid upsetting original document from a Content Security point of view + unattachedDoc = document.implementation.createHTMLDocument(); + } catch (e) { + // fallback to more direct method + unattachedDoc = this.doc; + } switch (m.type) { case 'characterData': { const value = m.target.textContent; @@ -554,7 +562,7 @@ export default class MutationBuffer { } if (attributeName === 'style') { - const old = this.doc.createElement('span'); + const old = unattachedDoc.createElement('span'); if (m.oldValue) { old.setAttribute('style', m.oldValue); } From a606109519d9f834d34a2356deb6723f543a0caa Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 2 Jun 2023 19:25:49 +1000 Subject: [PATCH 194/201] fix turbo dev command errors (#1229) * fix turbo dev command errors * Create forty-elephants-attack.md --------- Co-authored-by: Justin Halsall --- .changeset/forty-elephants-attack.md | 2 ++ turbo.json | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/forty-elephants-attack.md diff --git a/.changeset/forty-elephants-attack.md b/.changeset/forty-elephants-attack.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/forty-elephants-attack.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/turbo.json b/turbo.json index 3908cac7ef..c14def8617 100644 --- a/turbo.json +++ b/turbo.json @@ -7,8 +7,13 @@ "outputs": ["lib/**", "es/**", "dist/**", "typings/**"] }, "test": {}, - "test:watch": {}, - "dev": {}, + "test:watch": { + "persistent": true + }, + "dev": { + "dependsOn": ["^prepublish"], + "persistent": true + }, "lint": {}, "check-types": {} } From b798f2dbc07b5a24dcaf40d164159200b6c0679d Mon Sep 17 00:00:00 2001 From: Yun Feng Date: Fri, 2 Jun 2023 19:44:00 +1000 Subject: [PATCH 195/201] Fix rrdom bugs (#1222) * fix: rrdom bug 1. fix one bug of the diff algorithm 2. omit srcdoc attribute of iframe * use linked list to iterate children * fix the bug that the children of shadowRoot don't get diffed * add test cases * add change log --- .changeset/cold-eyes-hunt.md | 8 ++++ packages/rrdom/src/diff.ts | 76 ++++++++++++++------------------ packages/rrdom/test/diff.test.ts | 74 +++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 43 deletions(-) create mode 100644 .changeset/cold-eyes-hunt.md diff --git a/.changeset/cold-eyes-hunt.md b/.changeset/cold-eyes-hunt.md new file mode 100644 index 0000000000..ac3a8be10d --- /dev/null +++ b/.changeset/cold-eyes-hunt.md @@ -0,0 +1,8 @@ +--- +'rrdom': patch +--- + +Fix: rrdom bugs + +1. Fix a bug in the diff algorithm. +2. Omit the 'srcdoc' attribute of iframes to avoid overwriting content. diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 78628988e0..f37f298106 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -116,17 +116,7 @@ export function diff( rrnodeMirror, ); - const oldChildren = oldTree.childNodes; - const newChildren = newTree.childNodes; - if (oldChildren.length > 0 || newChildren.length > 0) { - diffChildren( - Array.from(oldChildren), - newChildren, - oldTree, - replayer, - rrnodeMirror, - ); - } + diffChildren(oldTree, newTree, replayer, rrnodeMirror); diffAfterUpdatingChildren(oldTree, newTree, replayer, rrnodeMirror); } @@ -196,18 +186,13 @@ function diffBeforeUpdatingChildren( } if (newRRElement.shadowRoot) { if (!oldElement.shadowRoot) oldElement.attachShadow({ mode: 'open' }); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const oldChildren = oldElement.shadowRoot!.childNodes; - const newChildren = newRRElement.shadowRoot.childNodes; - if (oldChildren.length > 0 || newChildren.length > 0) - diffChildren( - Array.from(oldChildren), - newChildren, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - oldElement.shadowRoot!, - replayer, - rrnodeMirror, - ); + diffChildren( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + oldElement.shadowRoot!, + newRRElement.shadowRoot, + replayer, + rrnodeMirror, + ); } break; } @@ -335,7 +320,8 @@ function diffProps( ctx.drawImage(image, 0, 0, image.width, image.height); } }; - } else oldTree.setAttribute(name, newValue); + } else if (newTree.tagName === 'IFRAME' && name === 'srcdoc') continue; + else oldTree.setAttribute(name, newValue); } for (const { name } of Array.from(oldAttributes)) @@ -346,12 +332,14 @@ function diffProps( } function diffChildren( - oldChildren: (Node | undefined)[], - newChildren: IRRNode[], - parentNode: Node, + oldTree: Node, + newTree: IRRNode, replayer: ReplayerHandler, rrnodeMirror: Mirror, ) { + const oldChildren: (Node | undefined)[] = Array.from(oldTree.childNodes); + const newChildren = newTree.childNodes; + if (oldChildren.length === 0 && newChildren.length === 0) return; let oldStartIndex = 0, oldEndIndex = oldChildren.length - 1, newStartIndex = 0, @@ -371,14 +359,12 @@ function diffChildren( // same first node? nodeMatching(oldStartNode, newStartNode, replayer.mirror, rrnodeMirror) ) { - diff(oldStartNode, newStartNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newStartNode = newChildren[++newStartIndex]; } else if ( // same last node? nodeMatching(oldEndNode, newEndNode, replayer.mirror, rrnodeMirror) ) { - diff(oldEndNode, newEndNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( @@ -386,11 +372,10 @@ function diffChildren( nodeMatching(oldStartNode, newEndNode, replayer.mirror, rrnodeMirror) ) { try { - parentNode.insertBefore(oldStartNode, oldEndNode.nextSibling); + oldTree.insertBefore(oldStartNode, oldEndNode.nextSibling); } catch (e) { console.warn(e); } - diff(oldStartNode, newEndNode, replayer, rrnodeMirror); oldStartNode = oldChildren[++oldStartIndex]; newEndNode = newChildren[--newEndIndex]; } else if ( @@ -398,11 +383,10 @@ function diffChildren( nodeMatching(oldEndNode, newStartNode, replayer.mirror, rrnodeMirror) ) { try { - parentNode.insertBefore(oldEndNode, oldStartNode); + oldTree.insertBefore(oldEndNode, oldStartNode); } catch (e) { console.warn(e); } - diff(oldEndNode, newStartNode, replayer, rrnodeMirror); oldEndNode = oldChildren[--oldEndIndex]; newStartNode = newChildren[++newStartIndex]; } else { @@ -424,11 +408,10 @@ function diffChildren( nodeMatching(nodeToMove, newStartNode, replayer.mirror, rrnodeMirror) ) { try { - parentNode.insertBefore(nodeToMove, oldStartNode); + oldTree.insertBefore(nodeToMove, oldStartNode); } catch (e) { console.warn(e); } - diff(nodeToMove, newStartNode, replayer, rrnodeMirror); oldChildren[indexInOld] = undefined; } else { const newNode = createOrGetNode( @@ -438,7 +421,7 @@ function diffChildren( ); if ( - parentNode.nodeName === '#document' && + oldTree.nodeName === '#document' && oldStartNode && /** * Special case 1: one document isn't allowed to have two doctype nodes at the same time, so we need to remove the old one first before inserting the new one. @@ -453,14 +436,13 @@ function diffChildren( (newNode.nodeType === newNode.ELEMENT_NODE && oldStartNode.nodeType === oldStartNode.ELEMENT_NODE)) ) { - parentNode.removeChild(oldStartNode); + oldTree.removeChild(oldStartNode); replayer.mirror.removeNodeFromMap(oldStartNode); oldStartNode = oldChildren[++oldStartIndex]; } try { - parentNode.insertBefore(newNode, oldStartNode || null); - diff(newNode, newStartNode, replayer, rrnodeMirror); + oldTree.insertBefore(newNode, oldStartNode || null); } catch (e) { console.warn(e); } @@ -482,8 +464,7 @@ function diffChildren( rrnodeMirror, ); try { - parentNode.insertBefore(newNode, referenceNode); - diff(newNode, newChildren[newStartIndex], replayer, rrnodeMirror); + oldTree.insertBefore(newNode, referenceNode); } catch (e) { console.warn(e); } @@ -491,15 +472,24 @@ function diffChildren( } else if (newStartIndex > newEndIndex) { for (; oldStartIndex <= oldEndIndex; oldStartIndex++) { const node = oldChildren[oldStartIndex]; - if (!node || node.parentNode !== parentNode) continue; + if (!node || node.parentNode !== oldTree) continue; try { - parentNode.removeChild(node); + oldTree.removeChild(node); replayer.mirror.removeNodeFromMap(node); } catch (e) { console.warn(e); } } } + + // Recursively diff the children of the old tree and the new tree with their props and deeper structures. + let oldChild = oldTree.firstChild; + let newChild = newTree.firstChild; + while (oldChild !== null && newChild !== null) { + diff(oldChild, newChild, replayer, rrnodeMirror); + oldChild = oldChild.nextSibling; + newChild = newChild.nextSibling; + } } export function createOrGetNode( diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index 04d638c314..3f18a6ee7e 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -15,6 +15,7 @@ import { Mirror as RRNodeMirror, RRDocument, RRMediaElement, + printRRDom, } from '../src'; import { createOrGetNode, @@ -106,6 +107,7 @@ function shuffle(list: number[]) { describe('diff algorithm for rrdom', () => { let mirror: NodeMirror; let replayer: ReplayerHandler; + let warn: jest.SpyInstance; beforeEach(() => { mirror = createMirror(); @@ -118,6 +120,14 @@ describe('diff algorithm for rrdom', () => { afterAppend: () => {}, }; document.write(''); + // Mock the original console.warn function to make the test fail once console.warn is called. + warn = jest.spyOn(console, 'warn'); + }); + + afterEach(() => { + // Check that warn was not called (fail on warning) + expect(warn).not.toBeCalled(); + warn.mockRestore(); }); describe('diff single node', () => { @@ -437,6 +447,19 @@ describe('diff algorithm for rrdom', () => { expect(document.createElement).toHaveBeenCalledWith('img'); jest.restoreAllMocks(); }); + + it('can omit srcdoc attribute of iframe element', () => { + // If srcdoc attribute is set, the content of iframe recorded by rrweb will be override. + const element = document.createElement('iframe'); + const rrDocument = new RRDocument(); + const rrIframe = rrDocument.createElement('iframe'); + const sn = Object.assign({}, elementSn, { tagName: 'iframe' }); + rrDocument.mirror.add(rrIframe, sn); + rrIframe.attributes['srcdoc'] = ''; + + diff(element, rrIframe, replayer); + expect(element.getAttribute('srcdoc')).toBe(null); + }); }); describe('diff children', () => { @@ -1054,6 +1077,57 @@ describe('diff algorithm for rrdom', () => { const liChild = spanChild.childNodes[0] as HTMLElement; expect(liChild.tagName).toEqual('LI'); }); + + it('should handle corner case with children removed during diff process', () => { + /** + * This test case is to simulate the following scenario: + * The old tree structure: + * 0 P + * 1 SPAN + * 2 SPAN + * The new tree structure: + * 0 P + * 1 SPAN + * 2 SPAN + * 3 SPAN + */ + const node = createTree( + { + tagName: 'p', + id: 0, + children: [1, 2].map((c) => ({ tagName: 'span', id: c })), + }, + undefined, + mirror, + ) as Node; + expect(node.childNodes.length).toEqual(2); + const rrdom = new RRDocument(); + const rrNode = createTree( + { + tagName: 'p', + id: 0, + children: [ + { tagName: 'span', id: 1, children: [{ tagName: 'span', id: 2 }] }, + { tagName: 'span', id: 3 }, + ], + }, + rrdom, + ) as RRNode; + expect(printRRDom(rrNode, rrdom.mirror)).toMatchInlineSnapshot(` + "0 P + 1 SPAN + 2 SPAN + 3 SPAN + " + `); + diff(node, rrNode, replayer); + + expect(node.childNodes.length).toEqual(2); + expect(node.childNodes[0].childNodes.length).toEqual(1); + expect(mirror.getId(node.childNodes[1])).toEqual(3); + expect(node.childNodes[0].childNodes.length).toEqual(1); + expect(mirror.getId(node.childNodes[0].childNodes[0])).toEqual(2); + }); }); describe('diff shadow dom', () => { From d7c72bff0724b46a6fa94af455220626a27104fe Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 2 Jun 2023 12:16:37 +0200 Subject: [PATCH 196/201] fix: Ensure attributes are lowercased when checking (#1183) * fix: Ensure attributes are lowercased when checking * add changeset * fix to lower case * Apply formatting changes --------- Co-authored-by: mydea --- .changeset/lovely-students-boil.md | 6 ++++++ packages/rrweb-snapshot/src/snapshot.ts | 11 ++++++----- packages/rrweb-snapshot/src/utils.ts | 8 ++++++-- packages/rrweb/src/record/mutation.ts | 5 +++-- packages/rrweb/src/record/observer.ts | 8 ++++++-- packages/rrweb/src/replay/index.ts | 3 ++- 6 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 .changeset/lovely-students-boil.md diff --git a/.changeset/lovely-students-boil.md b/.changeset/lovely-students-boil.md new file mode 100644 index 0000000000..9c710bf1c6 --- /dev/null +++ b/.changeset/lovely-students-boil.md @@ -0,0 +1,6 @@ +--- +'rrweb-snapshot': patch +'rrweb': patch +--- + +fix: Ensure attributes are lowercased when checking diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 0eeb289b4f..51f76f46f8 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -21,6 +21,7 @@ import { isNativeShadowDom, getCssRulesString, getInputType, + toLowerCase, } from './utils'; let _id = 1; @@ -32,12 +33,12 @@ export function genId(): number { return _id++; } -function getValidTagName(element: HTMLElement): string { +function getValidTagName(element: HTMLElement): Lowercase { if (element instanceof HTMLFormElement) { return 'form'; } - const processedTagName = element.tagName.toLowerCase().trim(); + const processedTagName = toLowerCase(element.tagName); if (tagNameRegex.test(processedTagName)) { // if the tag name is odd and we cannot extract @@ -222,8 +223,8 @@ function getHref() { export function transformAttribute( doc: Document, - tagName: string, - name: string, + tagName: Lowercase, + name: Lowercase, value: string | null, ): string | null { if (!value) { @@ -638,7 +639,7 @@ function serializeElementNode( attributes[attr.name] = transformAttribute( doc, tagName, - attr.name, + toLowerCase(attr.name), attr.value, ); } diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index c2f85e6e08..b124680b57 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -169,7 +169,7 @@ export function maskInputValue({ maskInputFn?: MaskInputFn; }): string { let text = value || ''; - const actualType = type && type.toLowerCase(); + const actualType = type && toLowerCase(type); if ( maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] || @@ -184,6 +184,10 @@ export function maskInputValue({ return text; } +export function toLowerCase(str: T): Lowercase { + return str.toLowerCase() as unknown as Lowercase; +} + const ORIGINAL_ATTRIBUTE_NAME = '__rrweb_original__'; type PatchedGetImageData = { [ORIGINAL_ATTRIBUTE_NAME]: CanvasImageData['getImageData']; @@ -265,6 +269,6 @@ export function getInputType(element: HTMLElement): Lowercase | null { ? 'password' : type ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - (type.toLowerCase() as Lowercase) + toLowerCase(type) : null; } diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index f352424b5f..39a6107635 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -9,6 +9,7 @@ import { Mirror, isNativeShadowDom, getInputType, + toLowerCase, } from 'rrweb-snapshot'; import type { observerParam, MutationBufferParam } from '../types'; import type { @@ -597,8 +598,8 @@ export default class MutationBuffer { // overwrite attribute if the mutations was triggered in same time item.attributes[attributeName] = transformAttribute( this.doc, - target.tagName, - attributeName, + toLowerCase(target.tagName), + toLowerCase(attributeName), value, ); } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index afd68f1864..754f92400b 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -3,6 +3,7 @@ import { maskInputValue, Mirror, getInputType, + toLowerCase, } from 'rrweb-snapshot'; import type { FontFaceSet } from 'css-font-loading-module'; import { @@ -309,13 +310,16 @@ function initMouseInteractionObserver({ disableMap[key] !== false, ) .forEach((eventKey: keyof typeof MouseInteractions) => { - let eventName = eventKey.toLowerCase(); + let eventName = toLowerCase(eventKey); const handler = getHandler(eventKey); if (window.PointerEvent) { switch (MouseInteractions[eventKey]) { case MouseInteractions.MouseDown: case MouseInteractions.MouseUp: - eventName = eventName.replace('mouse', 'pointer'); + eventName = eventName.replace( + 'mouse', + 'pointer', + ) as unknown as typeof eventName; break; case MouseInteractions.TouchStart: case MouseInteractions.TouchEnd: diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 1bc9c6a6a1..aac84c2783 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -8,6 +8,7 @@ import { createMirror, attributes, serializedElementNodeWithId, + toLowerCase, } from 'rrweb-snapshot'; import { RRDocument, @@ -1120,7 +1121,7 @@ export class Replayer { if (d.id === -1) { break; } - const event = new Event(MouseInteractions[d.type].toLowerCase()); + const event = new Event(toLowerCase(MouseInteractions[d.type])); const target = this.mirror.getNode(d.id); if (!target) { return this.debugNodeNotFound(d, d.id); From 325a9f093ebf9e5a6d0b0ad7620260df9fc2e202 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Fri, 2 Jun 2023 11:19:35 +0100 Subject: [PATCH 197/201] Update test commands (#1205) * As per @Yun Feng: everyone has npm installed globally but maybe not yarn * Add command to enable test result updating * Default to running tests HEADLESS on rrweb * Add command to build:all in a low memory environment * Add a 'retest' command for when the code hasn't changed, but you are working on the test cases * Add commands to reformat according to prettier. Named 'reformat' to indicate that we are doing a `--write` * Update package.json Co-authored-by: Yun Feng * Apply suggestions from code review Co-authored-by: Yun Feng * Create few-turkeys-reflect.md * Apply formatting changes --------- Co-authored-by: Justin Halsall Co-authored-by: Yun Feng --- .changeset/few-turkeys-reflect.md | 2 ++ README.md | 5 +++-- package.json | 4 +++- packages/rrweb-snapshot/package.json | 3 ++- packages/rrweb/package.json | 12 ++++++++---- turbo.json | 1 + 6 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 .changeset/few-turkeys-reflect.md diff --git a/.changeset/few-turkeys-reflect.md b/.changeset/few-turkeys-reflect.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/few-turkeys-reflect.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/README.md b/README.md index 491b7fb217..697cbcdabb 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,9 @@ Since we want the record and replay sides to share a strongly typed data structu 2. Run `yarn install` in the root to install required dependencies for all sub-packages (note: `npm install` is _not_ recommended). 3. Run `yarn dev` in the root to get auto-building for all the sub-packages whenever you modify anything. 4. Navigate to one of the sub-packages (in the `packages` folder) where you'd like to make a change. -5. Patch the code and run `yarn test` to run the tests, make sure they pass before you commit anything. -6. Push the code and create a pull request. +5. Patch the code and run `yarn test` to run the tests, make sure they pass before you commit anything. Add test cases in order to avoid future regression. +6. If tests are failing, but the change in output is desirable, run `yarn test:update` and carefully commit the changes in test output. +7. Push the code and create a pull request. Protip: You can run `yarn test` in the root folder to run all the tests. diff --git a/package.json b/package.json index 60096855a9..3e7cb6027c 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,11 @@ "typescript": "^4.7.3" }, "scripts": { - "build:all": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references' 'yarn turbo run prepublish'", + "build:all": "NODE_OPTIONS='--max-old-space-size=4096' yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references' 'yarn turbo run prepublish'", "test": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references --check' 'yarn turbo run test'", "test:watch": "yarn turbo run test:watch", + "test:update": "yarn turbo run test:update", + "format": "yarn prettier --write '**/*.{ts,md}'", "dev": "yarn turbo run dev", "repl": "cd packages/rrweb && npm run repl", "live-stream": "cd packages/rrweb && yarn live-stream", diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index e4d8f3b766..7dc4c4397a 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -7,11 +7,12 @@ "prepack": "npm run bundle && npm run typings", "test": "jest", "test:watch": "jest --watch", + "test:update": "jest --updateSnapshot", "bundle": "rollup --config", "bundle:es-only": "cross-env ES_ONLY=true rollup --config", "dev": "yarn bundle:es-only --watch", "typings": "tsc -d --declarationDir typings", - "prepublish": "npm run typings && npm run bundle", + "prepublish": "yarn typings && yarn bundle", "lint": "yarn eslint src" }, "type": "module", diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 9064f03e93..42d092aeed 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -5,10 +5,14 @@ "scripts": { "prepare": "npm run prepack", "prepack": "npm run bundle", - "test": "npm run bundle:browser && jest --testPathIgnorePatterns test/benchmark", - "test:headless": "PUPPETEER_HEADLESS=true npm run test", - "test:watch": "PUPPETEER_HEADLESS=true npm run test -- --watch", - "repl": "npm run bundle:browser && node scripts/repl.js", + "retest": "jest --testPathIgnorePatterns test/benchmark", + "build-and-test": "yarn bundle:browser && yarn retest", + "test:headless": "PUPPETEER_HEADLESS=true yarn build-and-test", + "test:headful": "PUPPETEER_HEADLESS=false yarn build-and-test", + "test": "yarn test:headless", + "test:watch": "yarn test:headless -- --watch", + "test:update": "yarn test:headless -- --updateSnapshot", + "repl": "yarn bundle:browser && node scripts/repl.js", "live-stream": "yarn bundle:browser && node scripts/stream.js", "dev": "yarn bundle:browser --watch", "bundle:browser": "cross-env BROWSER_ONLY=true rollup --config", diff --git a/turbo.json b/turbo.json index c14def8617..70ca7bb533 100644 --- a/turbo.json +++ b/turbo.json @@ -10,6 +10,7 @@ "test:watch": { "persistent": true }, + "test:update": {}, "dev": { "dependsOn": ["^prepublish"], "persistent": true From 490b3e2b62b62d61e6f6f5391d5b879194c9a221 Mon Sep 17 00:00:00 2001 From: Eoghan Murray Date: Fri, 2 Jun 2023 11:20:14 +0100 Subject: [PATCH 198/201] Guard against redefinition of Date.now (#1196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Guard against presence of likely older third party libraries which (re)define Date.now, e.g. https://github.com/datejs/Datejs/issues/92 * Apply formatting changes * (remove nowTimestamp import where Date.now() is not used) * Add a PURE marker so an empty `ìf` statement doesn't show up in the rrweb-replay output * Update packages/rrweb/src/utils.ts Fix typing issue with regex against `Date.now()` Co-authored-by: Justin Halsall * Create little-radios-thank.md * Apply formatting changes * Update .changeset/little-radios-thank.md * Apply formatting changes --------- Co-authored-by: eoghanmurray Co-authored-by: Justin Halsall --- .changeset/date-now-guard.md | 5 +++++ .changeset/little-radios-thank.md | 5 +++++ packages/rrweb/src/record/index.ts | 3 ++- packages/rrweb/src/record/observer.ts | 5 +++-- packages/rrweb/src/utils.ts | 9 +++++++++ 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 .changeset/date-now-guard.md create mode 100644 .changeset/little-radios-thank.md diff --git a/.changeset/date-now-guard.md b/.changeset/date-now-guard.md new file mode 100644 index 0000000000..2e0ac5e711 --- /dev/null +++ b/.changeset/date-now-guard.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Guard against presence of older 3rd party javascript libraries which redefine Date.now() diff --git a/.changeset/little-radios-thank.md b/.changeset/little-radios-thank.md new file mode 100644 index 0000000000..ea9c3f609d --- /dev/null +++ b/.changeset/little-radios-thank.md @@ -0,0 +1,5 @@ +--- +'rrweb': patch +--- + +Guard against redefinition of Date.now by third party libraries which are also present on a page alongside rrweb diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index c0e69a025f..563942312f 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -14,6 +14,7 @@ import { hasShadowRoot, isSerializedIframe, isSerializedStylesheet, + nowTimestamp, } from '../utils'; import type { recordOptions } from '../types'; import { @@ -42,7 +43,7 @@ import { function wrapEvent(e: event): eventWithTime { return { ...e, - timestamp: Date.now(), + timestamp: nowTimestamp(), }; } diff --git a/packages/rrweb/src/record/observer.ts b/packages/rrweb/src/record/observer.ts index 754f92400b..78690578ad 100644 --- a/packages/rrweb/src/record/observer.ts +++ b/packages/rrweb/src/record/observer.ts @@ -17,6 +17,7 @@ import { legacy_isTouchEvent, patch, StyleSheetMirror, + nowTimestamp, } from '../utils'; import type { observerParam, MutationBufferParam } from '../types'; import { @@ -181,13 +182,13 @@ function initMoveObserver({ ? evt.changedTouches[0] : evt; if (!timeBaseline) { - timeBaseline = Date.now(); + timeBaseline = nowTimestamp(); } positions.push({ x: clientX, y: clientY, id: mirror.getId(target as Node), - timeOffset: Date.now() - timeBaseline, + timeOffset: nowTimestamp() - timeBaseline, }); // it is possible DragEvent is undefined even on devices // that support event 'drag' diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index 2197796258..604c8810e2 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -168,6 +168,15 @@ export function patch( } } +// guard against old third party libraries which redefine Date.now +let nowTimestamp = Date.now; + +if (!(/*@__PURE__*/ /[1-9][0-9]{12}/.test(Date.now().toString()))) { + // they have already redefined it! use a fallback + nowTimestamp = () => new Date().getTime(); +} +export { nowTimestamp }; + export function getWindowScroll(win: Window) { const doc = win.document; return { From a01a12ef6769f26aa922ccd6ac76499f0837f0c2 Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Fri, 30 Jun 2023 02:35:38 +0200 Subject: [PATCH 199/201] Fix types in rrwebPlayer (#1247) * Export correct mirror in player Was using DeprecatedMirror in its types, that shouldn't be used anymore. * Add playRange to types * Create smooth-poems-bake.md * Apply formatting changes * Add $set to player type * Update smooth-poems-bake.md * Apply formatting changes * Last two arguments of playRange are optional --- .changeset/smooth-poems-bake.md | 5 +++++ packages/rrweb-player/typings/index.d.ts | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changeset/smooth-poems-bake.md diff --git a/.changeset/smooth-poems-bake.md b/.changeset/smooth-poems-bake.md new file mode 100644 index 0000000000..9d4fac8ff6 --- /dev/null +++ b/.changeset/smooth-poems-bake.md @@ -0,0 +1,5 @@ +--- +'rrweb-player': patch +--- + +Fix `player.getMirror`, `player.playRange`, `player.$set` types in rrwebPlayer diff --git a/packages/rrweb-player/typings/index.d.ts b/packages/rrweb-player/typings/index.d.ts index b27799aa88..e76b493e6f 100755 --- a/packages/rrweb-player/typings/index.d.ts +++ b/packages/rrweb-player/typings/index.d.ts @@ -1,6 +1,7 @@ import { playerConfig } from 'rrweb/typings/types'; import { eventWithTime } from '@rrweb/types'; -import { Replayer, mirror } from 'rrweb'; +import { Replayer } from 'rrweb'; +import { Mirror } from 'rrweb-snapshot'; import { SvelteComponent } from 'svelte'; export type RRwebPlayerOptions = { @@ -67,13 +68,20 @@ export default class rrwebPlayer extends SvelteComponent { addEvent(event: eventWithTime): void; getMetaData: Replayer['getMetaData']; getReplayer: () => Replayer; - getMirror: () => typeof mirror; + getMirror: () => Mirror; toggle: () => void; setSpeed: (speed: number) => void; toggleSkipInactive: () => void; triggerResize: () => void; + $set: (options: { width: number; height: number }) => void; play: () => void; pause: () => void; goto: (timeOffset: number, play?: boolean) => void; + playRange: ( + timeOffset: number, + endTimeOffset: number, + startLooping?: boolean, + afterHook?: undefined | (() => void), + ) => void; } From 46df5cd988357eee1ebc4f86e27ac47608b30f1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:54:51 +0200 Subject: [PATCH 200/201] Version Packages (alpha) (#1216) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 13 +++++++++++++ packages/rrdom-nodejs/CHANGELOG.md | 8 ++++++++ packages/rrdom-nodejs/package.json | 6 +++--- packages/rrdom/CHANGELOG.md | 12 ++++++++++++ packages/rrdom/package.json | 6 +++--- packages/rrvideo/CHANGELOG.md | 9 +++++++++ packages/rrvideo/package.json | 6 +++--- packages/rrweb-player/CHANGELOG.md | 9 +++++++++ packages/rrweb-player/package.json | 6 +++--- packages/rrweb-snapshot/CHANGELOG.md | 6 ++++++ packages/rrweb-snapshot/package.json | 2 +- packages/rrweb/CHANGELOG.md | 19 +++++++++++++++++++ packages/rrweb/package.json | 8 ++++---- packages/types/CHANGELOG.md | 7 +++++++ packages/types/package.json | 4 ++-- packages/web-extension/CHANGELOG.md | 8 ++++++++ packages/web-extension/package.json | 8 ++++---- 17 files changed, 114 insertions(+), 23 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index c0b7958b10..c9cc65e0ca 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -12,20 +12,31 @@ "rrvideo": "2.0.0-alpha.6" }, "changesets": [ + "brave-numbers-joke", "calm-bulldogs-speak", "chatty-cherries-train", + "cold-eyes-hunt", "controller-finish-flag", + "date-now-guard", "eight-terms-hunt", "empty-bikes-cheer", "fair-dragons-greet", "fast-chefs-smell", + "few-turkeys-reflect", "five-peas-lay", + "forty-elephants-attack", + "fresh-cars-impress", "fresh-spoons-drive", + "gold-terms-look", "grumpy-ways-own", "large-ants-prove", + "lazy-toes-confess", + "little-radios-thank", "little-suits-leave", "loud-seals-raise", "lovely-pears-cross", + "lovely-students-boil", + "mean-tips-impress", "mighty-frogs-sparkle", "nervous-poets-grin", "nervous-tables-travel", @@ -37,9 +48,11 @@ "proud-experts-jam", "real-masks-explode", "real-trains-switch", + "rich-crews-protect", "serious-ants-juggle", "sixty-impalas-laugh", "small-olives-arrive", + "smooth-poems-bake", "stupid-ghosts-help", "swift-peas-film", "tidy-yaks-joke", diff --git a/packages/rrdom-nodejs/CHANGELOG.md b/packages/rrdom-nodejs/CHANGELOG.md index 594a60a69b..4fe4518e12 100644 --- a/packages/rrdom-nodejs/CHANGELOG.md +++ b/packages/rrdom-nodejs/CHANGELOG.md @@ -1,5 +1,13 @@ # rrdom-nodejs +## 2.0.0-alpha.9 + +### Patch Changes + +- Updated dependencies [[`b798f2d`](https://github.com/rrweb-io/rrweb/commit/b798f2dbc07b5a24dcaf40d164159200b6c0679d), [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe)]: + - rrdom@2.0.0-alpha.9 + - rrweb-snapshot@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Patch Changes diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index eee9d733e5..9e2b8d23b2 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "rrdom-nodejs", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "scripts": { "dev": "rollup -c -w", "bundle": "rollup --config", @@ -48,8 +48,8 @@ "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", - "rrdom": "^2.0.0-alpha.8", - "rrweb-snapshot": "^2.0.0-alpha.8" + "rrdom": "^2.0.0-alpha.9", + "rrweb-snapshot": "^2.0.0-alpha.9" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom/CHANGELOG.md b/packages/rrdom/CHANGELOG.md index a1675cb13d..fcfd27dd0c 100644 --- a/packages/rrdom/CHANGELOG.md +++ b/packages/rrdom/CHANGELOG.md @@ -1,5 +1,17 @@ # rrdom +## 2.0.0-alpha.9 + +### Patch Changes + +- [#1222](https://github.com/rrweb-io/rrweb/pull/1222) [`b798f2d`](https://github.com/rrweb-io/rrweb/commit/b798f2dbc07b5a24dcaf40d164159200b6c0679d) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Fix: rrdom bugs + + 1. Fix a bug in the diff algorithm. + 2. Omit the 'srcdoc' attribute of iframes to avoid overwriting content. + +- Updated dependencies [[`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe)]: + - rrweb-snapshot@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Patch Changes diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index b554693602..04bbbbad0a 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -1,6 +1,6 @@ { "name": "rrdom", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme", "license": "MIT", "main": "lib/rrdom.cjs", @@ -32,7 +32,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^20.0.0", - "@rrweb/types": "^2.0.0-alpha.8", + "@rrweb/types": "^2.0.0-alpha.9", "@types/jest": "^27.4.1", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", @@ -47,6 +47,6 @@ "ts-jest": "^27.1.3" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.8" + "rrweb-snapshot": "^2.0.0-alpha.9" } } diff --git a/packages/rrvideo/CHANGELOG.md b/packages/rrvideo/CHANGELOG.md index 10a4f5e0da..c603c1d329 100644 --- a/packages/rrvideo/CHANGELOG.md +++ b/packages/rrvideo/CHANGELOG.md @@ -1,5 +1,14 @@ # rrvideo +## 2.0.0-alpha.9 + +### Patch Changes + +- [#1197](https://github.com/rrweb-io/rrweb/pull/1197) [`23d0138`](https://github.com/rrweb-io/rrweb/commit/23d01387f439db68d2874879242b6ade3e103f75) Thanks [@YunFeng0817](https://github.com/YunFeng0817)! - Refactor: Improve the video quality and add a progress bar for the CLI tool + +- Updated dependencies [[`a01a12e`](https://github.com/rrweb-io/rrweb/commit/a01a12ef6769f26aa922ccd6ac76499f0837f0c2)]: + - rrweb-player@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Patch Changes diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index df6d5b37c0..c654da461f 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -1,6 +1,6 @@ { "name": "rrvideo", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "description": "transform rrweb session into video", "main": "build/index.js", "bin": { @@ -26,13 +26,13 @@ "@types/node": "^18.15.11", "jest": "^27.5.1", "ts-jest": "^27.1.3", - "@rrweb/types": "^2.0.0-alpha.8" + "@rrweb/types": "^2.0.0-alpha.9" }, "dependencies": { "@open-tech-world/cli-progress-bar": "^2.0.2", "fs-extra": "^11.1.1", "minimist": "^1.2.5", "playwright": "^1.32.1", - "rrweb-player": "^2.0.0-alpha.8" + "rrweb-player": "^2.0.0-alpha.9" } } diff --git a/packages/rrweb-player/CHANGELOG.md b/packages/rrweb-player/CHANGELOG.md index 04e03595b1..6c5cfe50fe 100644 --- a/packages/rrweb-player/CHANGELOG.md +++ b/packages/rrweb-player/CHANGELOG.md @@ -1,5 +1,14 @@ # rrweb-player +## 2.0.0-alpha.9 + +### Patch Changes + +- [#1247](https://github.com/rrweb-io/rrweb/pull/1247) [`a01a12e`](https://github.com/rrweb-io/rrweb/commit/a01a12ef6769f26aa922ccd6ac76499f0837f0c2) Thanks [@Juice10](https://github.com/Juice10)! - Fix `player.getMirror`, `player.playRange`, `player.$set` types in rrwebPlayer + +- Updated dependencies [[`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`a1ec9a2`](https://github.com/rrweb-io/rrweb/commit/a1ec9a273e6634eec67098fdd880ee681648fbbd), [`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe), [`ebcbe8b`](https://github.com/rrweb-io/rrweb/commit/ebcbe8b0d746a0a4c07d3530387f920900f35215)]: + - rrweb@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Patch Changes diff --git a/packages/rrweb-player/package.json b/packages/rrweb-player/package.json index e8d61cd44f..5536cc6b12 100644 --- a/packages/rrweb-player/package.json +++ b/packages/rrweb-player/package.json @@ -1,10 +1,10 @@ { "name": "rrweb-player", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "devDependencies": { "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-node-resolve": "^13.2.1", - "@rrweb/types": "^2.0.0-alpha.8", + "@rrweb/types": "^2.0.0-alpha.9", "@types/offscreencanvas": "^2019.6.4", "eslint-config-google": "^0.14.0", "eslint-plugin-svelte3": "^4.0.0", @@ -24,7 +24,7 @@ }, "dependencies": { "@tsconfig/svelte": "^1.0.0", - "rrweb": "^2.0.0-alpha.8" + "rrweb": "^2.0.0-alpha.9" }, "scripts": { "build": "rollup -c", diff --git a/packages/rrweb-snapshot/CHANGELOG.md b/packages/rrweb-snapshot/CHANGELOG.md index 408bbeaebe..3ac44a0032 100644 --- a/packages/rrweb-snapshot/CHANGELOG.md +++ b/packages/rrweb-snapshot/CHANGELOG.md @@ -1,5 +1,11 @@ # rrweb-snapshot +## 2.0.0-alpha.9 + +### Patch Changes + +- [#1183](https://github.com/rrweb-io/rrweb/pull/1183) [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe) Thanks [@mydea](https://github.com/mydea)! - fix: Ensure attributes are lowercased when checking + ## 2.0.0-alpha.8 ### Minor Changes diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 7dc4c4397a..4fc2fda5d6 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "rrweb-snapshot", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "description": "rrweb's component to take a snapshot of DOM, aka DOM serializer", "scripts": { "prepare": "npm run prepack", diff --git a/packages/rrweb/CHANGELOG.md b/packages/rrweb/CHANGELOG.md index aeb7c4dbbc..7eed53c2b3 100644 --- a/packages/rrweb/CHANGELOG.md +++ b/packages/rrweb/CHANGELOG.md @@ -1,5 +1,24 @@ # rrweb +## 2.0.0-alpha.9 + +### Patch Changes + +- [#1196](https://github.com/rrweb-io/rrweb/pull/1196) [`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Guard against presence of older 3rd party javascript libraries which redefine Date.now() + +- [#1220](https://github.com/rrweb-io/rrweb/pull/1220) [`a1ec9a2`](https://github.com/rrweb-io/rrweb/commit/a1ec9a273e6634eec67098fdd880ee681648fbbd) Thanks [@wfk007](https://github.com/wfk007)! - perf: optimize performance of the DoubleLinkedList get + +- [#1196](https://github.com/rrweb-io/rrweb/pull/1196) [`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221) Thanks [@eoghanmurray](https://github.com/eoghanmurray)! - Guard against redefinition of Date.now by third party libraries which are also present on a page alongside rrweb + +- [#1183](https://github.com/rrweb-io/rrweb/pull/1183) [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe) Thanks [@mydea](https://github.com/mydea)! - fix: Ensure attributes are lowercased when checking + +- [#1214](https://github.com/rrweb-io/rrweb/pull/1214) [`ebcbe8b`](https://github.com/rrweb-io/rrweb/commit/ebcbe8b0d746a0a4c07d3530387f920900f35215) Thanks [@wfk007](https://github.com/wfk007)! - perf: optimize the performance of record in processMutation phase + +- Updated dependencies [[`b798f2d`](https://github.com/rrweb-io/rrweb/commit/b798f2dbc07b5a24dcaf40d164159200b6c0679d), [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe)]: + - rrdom@2.0.0-alpha.9 + - rrweb-snapshot@2.0.0-alpha.9 + - @rrweb/types@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Minor Changes diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 42d092aeed..9b50b2fdb4 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -1,6 +1,6 @@ { "name": "rrweb", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "description": "record and replay the web", "scripts": { "prepare": "npm run prepack", @@ -81,13 +81,13 @@ "tslib": "^2.3.1" }, "dependencies": { - "@rrweb/types": "^2.0.0-alpha.8", + "@rrweb/types": "^2.0.0-alpha.9", "@types/css-font-loading-module": "0.0.7", "@xstate/fsm": "^1.4.0", "base64-arraybuffer": "^1.0.1", "fflate": "^0.4.4", "mitt": "^3.0.0", - "rrdom": "^2.0.0-alpha.8", - "rrweb-snapshot": "^2.0.0-alpha.8" + "rrdom": "^2.0.0-alpha.9", + "rrweb-snapshot": "^2.0.0-alpha.9" } } diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 7ce37d89b6..c25deee87b 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,12 @@ # @rrweb/types +## 2.0.0-alpha.9 + +### Patch Changes + +- Updated dependencies [[`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe)]: + - rrweb-snapshot@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Minor Changes diff --git a/packages/types/package.json b/packages/types/package.json index d06eb0c01b..c438396a99 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@rrweb/types", - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "publishConfig": { "access": "public" }, @@ -43,7 +43,7 @@ "vite-plugin-dts": "^1.6.6" }, "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.8" + "rrweb-snapshot": "^2.0.0-alpha.9" }, "browserslist": [ "supports es6-class" diff --git a/packages/web-extension/CHANGELOG.md b/packages/web-extension/CHANGELOG.md index 7959bf4399..92661a43ac 100644 --- a/packages/web-extension/CHANGELOG.md +++ b/packages/web-extension/CHANGELOG.md @@ -1,5 +1,13 @@ # @rrweb/web-extension +## 2.0.0-alpha.9 + +### Patch Changes + +- Updated dependencies [[`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`a1ec9a2`](https://github.com/rrweb-io/rrweb/commit/a1ec9a273e6634eec67098fdd880ee681648fbbd), [`490b3e2`](https://github.com/rrweb-io/rrweb/commit/490b3e2b62b62d61e6f6f5391d5b879194c9a221), [`d7c72bf`](https://github.com/rrweb-io/rrweb/commit/d7c72bff0724b46a6fa94af455220626a27104fe), [`ebcbe8b`](https://github.com/rrweb-io/rrweb/commit/ebcbe8b0d746a0a4c07d3530387f920900f35215), [`a01a12e`](https://github.com/rrweb-io/rrweb/commit/a01a12ef6769f26aa922ccd6ac76499f0837f0c2)]: + - rrweb@2.0.0-alpha.9 + - rrweb-player@2.0.0-alpha.9 + ## 2.0.0-alpha.8 ### Patch Changes diff --git a/packages/web-extension/package.json b/packages/web-extension/package.json index 9d1666ab2c..2e438d485a 100644 --- a/packages/web-extension/package.json +++ b/packages/web-extension/package.json @@ -1,7 +1,7 @@ { "name": "@rrweb/web-extension", "private": true, - "version": "2.0.0-alpha.8", + "version": "2.0.0-alpha.9", "description": "The web extension of rrweb which helps to run rrweb on any website out of box", "author": "rrweb-io", "license": "MIT", @@ -16,7 +16,7 @@ "prepublish": "npm run pack:chrome && npm run pack:firefox" }, "devDependencies": { - "@rrweb/types": "^2.0.0-alpha.8", + "@rrweb/types": "^2.0.0-alpha.9", "@types/react-dom": "^18.0.6", "@types/webextension-polyfill": "^0.9.1", "@vitejs/plugin-react": "^2.1.0", @@ -40,7 +40,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.4.0", "react-router-dom": "^6.4.1", - "rrweb": "^2.0.0-alpha.8", - "rrweb-player": "^2.0.0-alpha.8" + "rrweb": "^2.0.0-alpha.9", + "rrweb-player": "^2.0.0-alpha.9" } } From 5124d4705b689f675c1b56ce408ad3182d60a114 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Wed, 12 Jul 2023 17:17:20 -0400 Subject: [PATCH 201/201] feat: Allow for masking of attributes [WIP] Note: this is more of an RFC This currently adds a new API, `maskAttributesFn: (key: string, value: string) => string`, that is used as a callback in `transformAttribute`. I prefer this API as it gives more flexibility for users (though it may need to pass the el node for most flexibility), but it is inconsistent with `maskTextFn` and `maskInputFn`. other options: * Rename this to something else (open to ideas) * Change this to pass value, and dom element (similar to MaskInputFn) to customize masking instead of decision maker of when to mask and introduce a simpler declarative API for what attributes to mask * ??? --- guide.md | 1 + packages/rrweb-snapshot/src/snapshot.ts | 22 + packages/rrweb-snapshot/src/types.ts | 1 + packages/rrweb-snapshot/test/snapshot.test.ts | 18 + packages/rrweb/src/record/index.ts | 4 + packages/rrweb/src/record/mutation.ts | 4 + packages/rrweb/src/types.ts | 4 + .../__snapshots__/integration.test.ts.snap | 789 +++++++++++++++++- packages/rrweb/test/html/form.html | 2 +- packages/rrweb/test/integration.test.ts | 52 ++ .../cross-origin-iframes.test.ts.snap | 8 +- packages/rrweb/test/utils.ts | 1 + 12 files changed, 860 insertions(+), 46 deletions(-) diff --git a/guide.md b/guide.md index e2dbf0d23f..07d90fe845 100644 --- a/guide.md +++ b/guide.md @@ -148,6 +148,7 @@ The parameter of `rrweb.record` accepts the following options. | maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter | | maskAllInputs | false | mask all input content as \* | | maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) | +| maskAttributeFn | - | callback before transforming attribute. can be used to mask specific attributes | maskInputFn | - | customize mask input content recording logic | | maskTextFn | - | customize mask text content recording logic | | slimDOMOptions | {} | remove unnecessary parts of the DOM
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) | diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 51f76f46f8..c7b2a2f0af 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -11,6 +11,7 @@ import { KeepIframeSrcFn, ICanvas, serializedElementNodeWithId, + MaskAttributeFn, } from './types'; import { Mirror, @@ -226,6 +227,7 @@ export function transformAttribute( tagName: Lowercase, name: Lowercase, value: string | null, + maskAttributeFn: MaskAttributeFn | undefined, ): string | null { if (!value) { return value; @@ -254,6 +256,11 @@ export function transformAttribute( return absoluteToDoc(doc, value); } + // Custom attribute masking + if (typeof maskAttributeFn === 'function') { + return maskAttributeFn(name, value); + } + return value; } @@ -434,6 +441,7 @@ function serializeNode( mirror: Mirror; blockClass: string | RegExp; blockSelector: string | null; + maskAttributeFn: MaskAttributeFn | undefined; maskTextClass: string | RegExp; maskTextSelector: string | null; inlineStylesheet: boolean; @@ -455,6 +463,7 @@ function serializeNode( mirror, blockClass, blockSelector, + maskAttributeFn, maskTextClass, maskTextSelector, inlineStylesheet, @@ -497,6 +506,7 @@ function serializeNode( blockClass, blockSelector, inlineStylesheet, + maskAttributeFn, maskInputOptions, maskInputFn, dataURLOptions, @@ -602,6 +612,7 @@ function serializeElementNode( blockClass: string | RegExp; blockSelector: string | null; inlineStylesheet: boolean; + maskAttributeFn: MaskAttributeFn | undefined; maskInputOptions: MaskInputOptions; maskInputFn: MaskInputFn | undefined; dataURLOptions?: DataURLOptions; @@ -621,6 +632,7 @@ function serializeElementNode( blockSelector, inlineStylesheet, maskInputOptions = {}, + maskAttributeFn, maskInputFn, dataURLOptions = {}, inlineImages, @@ -641,6 +653,7 @@ function serializeElementNode( tagName, toLowerCase(attr.name), attr.value, + maskAttributeFn, ); } } @@ -935,6 +948,7 @@ export function serializeNodeWithId( inlineStylesheet: boolean; newlyAddedElement?: boolean; maskInputOptions?: MaskInputOptions; + maskAttributeFn: MaskAttributeFn | undefined; maskTextFn: MaskTextFn | undefined; maskInputFn: MaskInputFn | undefined; slimDOMOptions: SlimDOMOptions; @@ -966,6 +980,7 @@ export function serializeNodeWithId( skipChild = false, inlineStylesheet = true, maskInputOptions = {}, + maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, @@ -990,6 +1005,7 @@ export function serializeNodeWithId( maskTextSelector, inlineStylesheet, maskInputOptions, + maskAttributeFn, maskTextFn, maskInputFn, dataURLOptions, @@ -1063,6 +1079,7 @@ export function serializeNodeWithId( skipChild, inlineStylesheet, maskInputOptions, + maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, @@ -1123,6 +1140,7 @@ export function serializeNodeWithId( skipChild: false, inlineStylesheet, maskInputOptions, + maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, @@ -1170,6 +1188,7 @@ export function serializeNodeWithId( skipChild: false, inlineStylesheet, maskInputOptions, + maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, @@ -1210,6 +1229,7 @@ function snapshot( maskTextSelector?: string | null; inlineStylesheet?: boolean; maskAllInputs?: boolean | MaskInputOptions; + maskAttributeFn?: MaskAttributeFn; maskTextFn?: MaskTextFn; maskInputFn?: MaskTextFn; slimDOM?: 'all' | boolean | SlimDOMOptions; @@ -1241,6 +1261,7 @@ function snapshot( inlineImages = false, recordCanvas = false, maskAllInputs = false, + maskAttributeFn, maskTextFn, maskInputFn, slimDOM = false, @@ -1306,6 +1327,7 @@ function snapshot( skipChild: false, inlineStylesheet, maskInputOptions, + maskAttributeFn, maskTextFn, maskInputFn, slimDOMOptions, diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 9edb4dd6d4..261e738121 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -155,6 +155,7 @@ export type DataURLOptions = Partial<{ export type MaskTextFn = (text: string) => string; export type MaskInputFn = (text: string, element: HTMLElement) => string; +export type MaskAttributeFn = (attributeName: string, attributeValue: string) => string; export type KeepIframeSrcFn = (src: string) => boolean; diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index 75d635e0c0..9be064e54c 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -5,6 +5,7 @@ import { JSDOM } from 'jsdom'; import { absoluteToStylesheet, serializeNodeWithId, + transformAttribute, _isBlockedElement, } from '../src/snapshot'; import { serializedNodeWithId } from '../src/types'; @@ -110,6 +111,21 @@ describe('absolute url to stylesheet', () => { }); }); +describe('transformAttribute()', () => { + it('handles empty attribute value', () => { + expect(transformAttribute(document, 'a', 'data-loading', null, undefined)).toBe(null) + expect(transformAttribute(document, 'a', 'data-loading', '', undefined)).toBe('') + }) + + it('handles custom masking function', () => { + const maskAttributeFn = jest.fn().mockImplementation((_key, value): string => { + return value.split('').reverse().join(''); + }) as any; + expect(transformAttribute(document, 'a', 'data-loading', 'foo', maskAttributeFn)).toBe('oof') + expect(maskAttributeFn).toHaveBeenCalledTimes(1); + }) +}) + describe('isBlockedElement()', () => { const subject = (html: string, opt: any = {}) => _isBlockedElement(render(html), 'rr-block', opt.blockSelector); @@ -147,6 +163,7 @@ describe('style elements', () => { maskTextSelector: null, skipChild: false, inlineStylesheet: true, + maskAttributeFn: undefined, maskTextFn: undefined, maskInputFn: undefined, slimDOMOptions: {}, @@ -192,6 +209,7 @@ describe('scrollTop/scrollLeft', () => { maskTextSelector: null, skipChild: false, inlineStylesheet: true, + maskAttributeFn: undefined, maskTextFn: undefined, maskInputFn: undefined, slimDOMOptions: {}, diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 563942312f..fb6bde26c3 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -70,6 +70,7 @@ function record( maskAllInputs, maskInputOptions: _maskInputOptions, slimDOMOptions: _slimDOMOptions, + maskAttributeFn, maskInputFn, maskTextFn, hooks, @@ -328,6 +329,7 @@ function record( inlineStylesheet, maskInputOptions, dataURLOptions, + maskAttributeFn, maskTextFn, maskInputFn, recordCanvas, @@ -370,6 +372,7 @@ function record( maskTextSelector, inlineStylesheet, maskAllInputs: maskInputOptions, + maskAttributeFn, maskTextFn, slimDOM: slimDOMOptions, dataURLOptions, @@ -532,6 +535,7 @@ function record( userTriggeredOnInput, collectFonts, doc, + maskAttributeFn, maskInputFn, maskTextFn, keepIframeSrcFn, diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index 39a6107635..0a1e56e3bf 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -175,6 +175,7 @@ export default class MutationBuffer { private maskTextSelector: observerParam['maskTextSelector']; private inlineStylesheet: observerParam['inlineStylesheet']; private maskInputOptions: observerParam['maskInputOptions']; + private maskAttributeFn: observerParam['maskAttributeFn']; private maskTextFn: observerParam['maskTextFn']; private maskInputFn: observerParam['maskInputFn']; private keepIframeSrcFn: observerParam['keepIframeSrcFn']; @@ -200,6 +201,7 @@ export default class MutationBuffer { 'maskTextSelector', 'inlineStylesheet', 'maskInputOptions', + 'maskAttributeFn', 'maskTextFn', 'maskInputFn', 'keepIframeSrcFn', @@ -303,6 +305,7 @@ export default class MutationBuffer { newlyAddedElement: true, inlineStylesheet: this.inlineStylesheet, maskInputOptions: this.maskInputOptions, + maskAttributeFn: this.maskAttributeFn, maskTextFn: this.maskTextFn, maskInputFn: this.maskInputFn, slimDOMOptions: this.slimDOMOptions, @@ -601,6 +604,7 @@ export default class MutationBuffer { toLowerCase(target.tagName), toLowerCase(attributeName), value, + this.maskAttributeFn, ); } break; diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index dd9a516709..e73ef50618 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -5,6 +5,7 @@ import type { MaskInputFn, MaskTextFn, DataURLOptions, + MaskAttributeFn, } from 'rrweb-snapshot'; import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; @@ -50,6 +51,7 @@ export type recordOptions = { maskTextSelector?: string; maskAllInputs?: boolean; maskInputOptions?: MaskInputOptions; + maskAttributeFn?: MaskAttributeFn; maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; slimDOMOptions?: SlimDOMOptions | 'all' | true; @@ -87,6 +89,7 @@ export type observerParam = { maskTextClass: maskTextClass; maskTextSelector: string | null; maskInputOptions: MaskInputOptions; + maskAttributeFn?: MaskAttributeFn; maskInputFn?: MaskInputFn; maskTextFn?: MaskTextFn; keepIframeSrcFn: KeepIframeSrcFn; @@ -130,6 +133,7 @@ export type MutationBufferParam = Pick< | 'maskTextSelector' | 'inlineStylesheet' | 'maskInputOptions' + | 'maskAttributeFn' | 'maskTextFn' | 'maskInputFn' | 'keepIframeSrcFn' diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index f244948b67..224572b1b4 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -600,6 +600,170 @@ exports[`record integration tests can freeze mutations 1`] = ` ]" `; +exports[`record integration tests can mask attribute on mutation 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 4 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 6 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"p\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"mutation observer\\", + \\"id\\": 8 + } + ], + \\"id\\": 7 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"ul\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"li\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 13 + } + ], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"canvas\\", + \\"attributes\\": {}, + \\"childNodes\\": [], + \\"id\\": 15 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 16 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 18 + } + ], + \\"id\\": 17 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\", + \\"id\\": 19 + } + ], + \\"id\\": 5 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [ + { + \\"parentId\\": 5, + \\"id\\": 10 + } + ], + \\"adds\\": [] + } + } +]" +`; + exports[`record integration tests can mask character data mutations 1`] = ` "[ { @@ -1764,7 +1928,9 @@ exports[`record integration tests can record form interactions 1`] = ` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"placeholder text\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -3545,7 +3711,9 @@ exports[`record integration tests can use maskInputOptions to configure which ty \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"placeholder text\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -5311,34 +5479,526 @@ exports[`record integration tests should handle recursive console messages 1`] = \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"Log record\\", - \\"id\\": 13 + \\"textContent\\": \\"Log record\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"id\\": 19 + } + ], + \\"id\\": 18 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", + \\"id\\": 20 + } + ], + \\"id\\": 16 + } + ], + \\"id\\": 3 + } + ], + \\"id\\": 1 + }, + \\"initialOffset\\": { + \\"left\\": 0, + \\"top\\": 0 + } + } + }, + { + \\"type\\": 6, + \\"data\\": { + \\"plugin\\": \\"rrweb/console@1\\", + \\"payload\\": { + \\"level\\": \\"log\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:20:21\\" + ], + \\"payload\\": [ + \\"\\\\\\"Proxied object:\\\\\\"\\", + \\"\\\\\\"[object Object]\\\\\\"\\" + ] + } + } + } +]" +`; + +exports[`record integration tests should mask attribute via function call 1`] = ` +"[ + { + \\"type\\": 0, + \\"data\\": {} + }, + { + \\"type\\": 1, + \\"data\\": {} + }, + { + \\"type\\": 4, + \\"data\\": { + \\"href\\": \\"about:blank\\", + \\"width\\": 1920, + \\"height\\": 1080 + } + }, + { + \\"type\\": 2, + \\"data\\": { + \\"node\\": { + \\"type\\": 0, + \\"childNodes\\": [ + { + \\"type\\": 1, + \\"name\\": \\"html\\", + \\"publicId\\": \\"\\", + \\"systemId\\": \\"\\", + \\"id\\": 2 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"html\\", + \\"attributes\\": { + \\"lang\\": \\"en\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 2, + \\"tagName\\": \\"head\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 5 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"id\\": 6 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 7 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"name\\": \\"viewport\\", + \\"content\\": \\"width=device-width, initial-scale=1.0\\" + }, + \\"childNodes\\": [], + \\"id\\": 8 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 9 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"ie=edge\\" + }, + \\"childNodes\\": [], + \\"id\\": 10 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 11 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"title\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"form fields\\", + \\"id\\": 13 + } + ], + \\"id\\": 12 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 14 + } + ], + \\"id\\": 4 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n\\\\n \\", + \\"id\\": 15 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"body\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 17 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"form\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 19 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"text\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 21 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"*********** ****\\" + }, + \\"childNodes\\": [], + \\"id\\": 22 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 23 + } + ], + \\"id\\": 20 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 24 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 26 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"on\\" + }, + \\"childNodes\\": [], + \\"id\\": 27 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 28 + } + ], + \\"id\\": 25 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 31 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"radio\\", + \\"name\\": \\"toggle\\", + \\"value\\": \\"off\\", + \\"checked\\": true + }, + \\"childNodes\\": [], + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 33 + } + ], + \\"id\\": 30 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 34 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"checkbox\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 36 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"checkbox\\" + }, + \\"childNodes\\": [], + \\"id\\": 37 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 38 + } + ], + \\"id\\": 35 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 39 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"textarea\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 41 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"textarea\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"cols\\": \\"30\\", + \\"rows\\": \\"10\\", + \\"data-unmask-example\\": \\"true\\" + }, + \\"childNodes\\": [], + \\"id\\": 42 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 43 + } + ], + \\"id\\": 40 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 44 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"select\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 46 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"select\\", + \\"attributes\\": { + \\"name\\": \\"\\", + \\"id\\": \\"\\", + \\"value\\": \\"1\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 48 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"1\\", + \\"selected\\": true + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"1\\", + \\"id\\": 50 + } + ], + \\"id\\": 49 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"option\\", + \\"attributes\\": { + \\"value\\": \\"2\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"2\\", + \\"id\\": 53 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 54 + } + ], + \\"id\\": 47 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 45 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 58 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 59 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 60 + } + ], + \\"id\\": 57 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 61 } ], - \\"id\\": 12 + \\"id\\": 18 }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 14 - } - ], - \\"id\\": 4 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 15 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"body\\", - \\"attributes\\": {}, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 17 + \\"textContent\\": \\"\\\\n \\\\n \\", + \\"id\\": 62 }, { \\"type\\": 2, @@ -5348,15 +6008,15 @@ exports[`record integration tests should handle recursive console messages 1`] = { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 19 + \\"id\\": 64 } ], - \\"id\\": 18 + \\"id\\": 63 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 20 + \\"id\\": 65 } ], \\"id\\": 16 @@ -5374,19 +6034,47 @@ exports[`record integration tests should handle recursive console messages 1`] = } }, { - \\"type\\": 6, + \\"type\\": 3, \\"data\\": { - \\"plugin\\": \\"rrweb/console@1\\", - \\"payload\\": { - \\"level\\": \\"log\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:20:21\\" - ], - \\"payload\\": [ - \\"\\\\\\"Proxied object:\\\\\\"\\", - \\"\\\\\\"[object Object]\\\\\\"\\" - ] - } + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"t\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"te\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"tes\\", + \\"isChecked\\": false, + \\"id\\": 22 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"test\\", + \\"isChecked\\": false, + \\"id\\": 22 } } ]" @@ -5546,7 +6234,9 @@ exports[`record integration tests should mask inputs via function call 1`] = ` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"placeholder text\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -8832,6 +9522,15 @@ exports[`record integration tests should not record input values if dynamically \\"id\\": 21 } }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 3, + \\"id\\": 21, + \\"x\\": 7, + \\"y\\": 0 + } + }, { \\"type\\": 3, \\"data\\": { @@ -9007,7 +9706,9 @@ exports[`record integration tests should not record input values if maskAllInput \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"placeholder text\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -12715,7 +13416,9 @@ exports[`record integration tests should record input userTriggered values if us \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"text\\" + \\"type\\": \\"text\\", + \\"title\\": \\"title text\\", + \\"placeholder\\": \\"placeholder text\\" }, \\"childNodes\\": [], \\"id\\": 22 diff --git a/packages/rrweb/test/html/form.html b/packages/rrweb/test/html/form.html index 9125e983fa..db1c127320 100644 --- a/packages/rrweb/test/html/form.html +++ b/packages/rrweb/test/html/form.html @@ -10,7 +10,7 @@

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